Stack Smashing Protection for Debian

Posted by Steve on Fri 23 Jun 2006 at 14:02

Since we last covered the use of Stack Smashing Protection (SSP) the default compiler for Debian Sid has been upgraded to include it, with no need for custom patching. Read on for a brief demonstration of how it can be used to prevent attacks.

The default C compiler for Sid, which will be used in Etch too, is GCC v4.1. This releasecontains the SSP patch which previously needed to be applied manually (we demonstrated applying this patch for GCC v3.4 a long time ago).Since the SSP patch is included in the compiler by default it is suddenly a lot easier to start working with it.

A vulnerable Program

Lets look at an example first of all, this is a common sample of a vulnerable C program:

#include <stdio.h>
#include <stdlib.h>

int main( int argc, char *argv[] )
{
   // Static buffer on the stack.
   char buffer[1024];

   if ( argc != 2 )
   {
      printf("Usage: %s argument\n", argv[0] );
      return( -1 );
   }
 
   // Unbound string copy.
   strcpy( buffer, argv[1]);

   printf( "Copied argument\n" );

   return(0);
}

This simple program accepts one argument and copies it into a static buffer. This is a classic programming mistake, and were this program compiled into a setuid/setgid binary it would easily allow an attacker to gain enhanced privileges.

Since we're going to highlight the new facilities of the newer compiler be sure to compile this example with gcc-3.3.

To test this program compile it as normal:

skx@desktop:/tmp$ gcc-3.3 -o buggy buggy.c

Now lets see if we can break it. First of all two test executions:

skx@desktop:/tmp$ /tmp/buggy
Usage: /tmp/buggy argument
skx@desktop:/tmp$ /tmp/buggy test
Copied argument

Both of these runs worked as expected. Now lets try passing a large argument to see if we can overflow the static buffer:

skx@desktop:/tmp$ ./buggy `perl -e 'print "X"x2048'`
Copied argument
Segmentation fault

Success: we overflowed the buffer with our 2k argument, and we resulted in a segmentation fault. If we can produce a core file we can debug this:

skx@desktop:/tmp$ ulimit -c 09999999
skx@desktop:/tmp$ ./buggy `perl -e 'print "X"x3333'`
Copied argument
Segmentation fault (core dumped)

Running gdb we can now look at the program:

skx@desktop:/tmp$ gdb ./buggy core
GNU gdb 6.4.90-debian
...
Program terminated with signal 11, Segmentation fault.
#0  0x58585858 in ?? ()
(gdb) info registers eip
eip            0x58585858       0x58585858

Here we see the instruction pointer is at 0x58585858 (the hex for 'X' is 0x58). This means we've effectively taken control of the binary with our malicious input.

From here to actually exploiting the binary to run a shell is trivial and can frequently be automated:

skx@desktop:~/cvs/cmd-overflow$ make
gcc-3.3 -o cmd-overflow -Wall -ggdb cmd-overflow.c
gcc-3.3 -o cmd-vuln -Wall -ggdb cmd-vuln.c
skx@desktop:~/cvs/cmd-overflow$ ./cmd-overflow --target=/tmp/buggy --args='%' --size=2048
Copied argument
sh-3.1$ id
uid=1000(skx) gid=1000(skx) groups=29(audio),44(video),46(plugdev),100(users),1000(skx)
sh-3.1$ exit
exit

Here we used a simple program to create an argument of length 2048 bytes which contains the code required to run a shell, then invoked our buggy program with this constructed argument.

The buffer was then overflowed and our code ran, this resulting in a shell being executed. ("Shellcode"). Had our buggy program been setuid we'd have gained root privileges!

Note:

Starting sometime in the life of the Linux 2.6.x kernel series a new security measure was introduced, to randomise heap addresses. If you're running such a kernel all of these examples will fail.

To disable this protection run:

root@desktop:~# sysctl -w kernel.randomize_va_space=0

This will allow you to experiment with buffer overflows, whilst avoiding the need to use advanced exploitation techniques. (Which can be a lot of fun if you're bored :)

Preventing This Attack With SSP

Now that Debian contains the 4.1 compiler we can use the new -fstack-protector argument to compile in automatic buffer overflow protection.

Before we do this we'll need to install a new package:

desktop:~# apt-get install libssp0-dev
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
  libssp0
Suggested packages:
  lib64ssp0
The following NEW packages will be installed
  libssp0 libssp0-dev

Once this is installed we can recompile our buggy program:

skx@desktop:/tmp$ gcc-4.1 -fstack-protector -o buggy buggy.c
buggy.c: In function ¡Æmain¡Ç:
buggy.c:16: warning: incompatible implicit declaration of built-in function ¡Æstrcpy¡Ç

This binary should now be protected against simple buffer overflows. Let us test it as we did before:

skx@desktop:/tmp$ ./buggy `perl -e 'print "x"x2048';`
Copied argument
*** stack smashing detected ***: xxxx .. xxxx terminated
Illegal instruction

Neat, huh?

Now we'll try to repeat the exploitation which succeeded previously:

skx@desktop:~/cvs/cmd-overflow$ ./cmd-overflow --target=/tmp/buggy --size=2048 --args='%'
Copied argument
*** stack smashing detected ***: terminated
Illegal instruction

It all looks good, and the protection works as designed. (Note that this protection will not help against "advanced" exploitation, such as return into libc.

Using this library you can add simple buffer overflow protection to your binaries with only a minor performance hit. If you maintain a package it might be worth rebuilding using this support to see how well it works in practise.

Rebuilding all the packages on security-critical host might be something worth trying, now that the tools are easily available.


This article can be found online at the Debian Administration website at the following bookmarkable URL (along with associated comments):

This article is copyright 2006 Steve - please ask for permission to republish or translate.