Adding stack smashing protection to GCC v3.4

Posted by Steve on Mon 10 Jan 2005 at 14:22

I maintain a copy of GCC which has the ability to detect and prevent buffer overflow attacks from working. This simple guide explains how my GCC packages are built, if you write code which offers a service across a network you might be interested in using something similar yourself.

Introduction

When it comes to buffer overflow protection there are a couple of different implementations which are available. One of the most tested and widely used is a patch from IBM which is known as SSP, (for "Stack Smashing Protection"). This patch was previously known as "ProPolice".

The IBM patch is available from its research homepage, although you won't need to fetch it from there if you're using Debian because the Debian GCC packages ship with the patch included in the source, although it's not enabled. This short recipe explains how you would enable it and rebuild your version of GCC.

What does it do?

Before you enable it you're probably wondering what does it do?

Well put simply it inserts extra code into your programs as you compile them, to warn against a buffer overflow exploit attempt.

Assuming that you have the following code:

void foo( char *user )
{
   char greeting[1024];

   sprintf(greeting, "Hello %s", user );
   printf( "%s", greeting );
}

(This is a bogus example).

Here the function foo takes a string argument which is appended to the end of the string "Hello ", without any length testing. If this input is supplied by a user then it can be used to overflow the "greeting" buffer - and execute code.

The way that the SSP detects this is to add some extra code similar to this:

void foo( char *user )
{
   int canary = 33;
   char greeting[1024];

   sprintf(greeting, "Hello %s", user );
   printf( "%s", greeting );

   if ( canary != 33 ) { printf( "Stack smashing attack thwarted\n" ); exit(-1); }
}

There is a new variable inserted on the local heap before the buffer - this has a random value assigned to it at runtime. Once the copy has occurred, but before the function has returned using a potentially overwritten return address this "canary" value is tested to make sure it contains what it should.

If the canary value has changed then we've detected a buffer overflow attempt, and this is logged before the program is terminated.

This will not catch all overflows, but it does raise the bar against most attackers.

Download GCC and Patch

Create a directory to contain the GCC source code, and download it with:

apt-get install dpkg-dev
apt-get source gcc-3.4

This will consume around 27Mb of space, and more will be required to actually build the source.

Once you've downloaded the source you'll be left with some files such as these:

drwxr-xr-x  7 root root     4096 Jan 10 13:18 gcc-3.4-3.4.3
-rw-r--r--  1 root root  5377788 Jan 10 13:17 gcc-3.4_3.4.3-7.diff.gz
-rw-r--r--  1 root root     2643 Jan 10 13:17 gcc-3.4_3.4.3-7.dsc
-rw-r--r--  1 root root 30513800 Nov 20 23:17 gcc-3.4_3.4.3.orig.tar.gz

The source to the package has been unpacked here into the directory gcc-3.4-3.4.3, so change into that directory and look around.

You'll soon discover that the source to GCC is still in a collection of compressed files, and the only real content is the debian/ subdirectory.

This debian directory contains all the files which we must modify, there are three things we need to change:

  • The build options to make sure that the patch is applied.
  • The version of the package to prevent it from being upgraded.
  • The version number of the compiler so we know we have it working.

The first is simple, open the file debian/rules.patch with your favourite editor and find the following lines:

# not applied by default
#debian_patches += protector

Uncomment the second leaving you with:

# not applied by default
debian_patches += protector

Now we need to change the version number of the package, do that by editing the file debian/changelog. Copy the top of the file and paste it in again - (the format of this file is well defined and it's not very forgiving of extra linebreaks, whitespace etc) - then modify the version number to add the letters ssp after the number:

gcc-3.4 (3.4.3-7ssp) unstable; urgency=low

  * Added SSP patch.

 -- Steve Kemp   Sat,  8 Jan 2005 10:57:39 +0100

The format of the changelog files is pretty strict, so you should copy the previous entry and just change the date + version number (the bit in brackets). Add in a suffix to whatever version you have ssp is what I use here.

The final thing is to update the version string which is output when you run "gcc --version", edit the file debian/patches/gcc-version", and look for the following line:

   sed -e '/version_string/s/"\([^"]*\)"/"\1 (Debian'"$pkgversion"')"/' \
      $dir/version.c > $dir/version.c.new \

I change it to read:

   sed -e '/version_string/s/"\([^"]*\)"/"\1 (Debian'"$pkgversion" SSP by Steve Kemp')"/' \
      $dir/version.c > $dir/version.c.new \

Now we've made all the changes we need.

Building GCC

Even on a powerful machine rebuilding GCC and all its components is going to take a long time, and use a lot of disk space.

Make sure you have all the packages which are required to rebuild gcc-3.4 installed by running:

apt-get build-dep gcc-3.4

Once that's done you are almost ready - you just need some utility scripts installed to handle the build, to get those run:

apt-get install devscripts build-essential

Now we can go! Whilst remaining inside the "debian/" directory run:

debuild

The build process will start, and you will see lots of progress and diagnostic output.

After a few hours hopefully all will be done and you'll have a lot of .deb files produced which you can install.

Testing It

Once you've built and installed the new version of gcc you should find that two new command line arguments are available:

-fstack-protector
  * Enables SSP protection.

-fno-stack-protector
  * Disables SSP protection.

The best way to see this working is to use it to compile something with and without the protection, and see how they compare.

Download test-ssp.c, which is a simple program based around the vulnerable example code we used above.

Compile this code twice, once with the protection and once without it:

cd /tmp
wget http://www.debian-administration.org/articles/75/test-ssp.c
gcc-3.4 -fno-stack-protector -o test-ssp1 test-ssp.c
gcc-3.4 -fstack-protector    -o test-ssp2 test-ssp.c

This will give you two binaries test-ssp1 and test-ssp2. Notice how they are almost identical in size?

root@undecided:/tmp# ls -l test-ssp*
-rw-r--r--  1 skx  users   839 Jan 10 14:25 test-ssp.c
-rwxr-xr-x  1 root root  12289 Jan 10 17:58 test-ssp1
-rwxr-xr-x  1 root root  15083 Jan 10 17:58 test-ssp2

Now try giving them both long arguments - say a 1234 character long argument, produced by running `perl -e 'print "x"x1234'`:

./test-ssp1 `perl -e 'print "x"x1234'`
Hello XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Segmentation fault

The program crashes after printing its greeting because the return address to the function was overwritten, and pointed to an invalid address

Now run the protected program identically:

./test-ssp2 `perl -e 'print "X"x1234'`
Hello xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
test-ssp: stack smashing attack in function fooAborted
Here's one I made earlier

On the other hand if you just want to use the compiler, not rebuild it yourself then you can use the package I rebuilt for unstable.

It usually lags around a week after GCC upgrades and can be installed by adding the following to your /etc/apt/sources.list file:

#
#  SSP / ProPolice GCC and supporting packages.
#
#
deb     http://people.debian.org/~skx/apt/sarge ./
deb-src http://people.debian.org/~skx/apt/sarge ./

 

 


Posted by ari (129.10.xx.xx) on Thu 10 Nov 2005 at 11:27
[ Send Message ]
How come this isn't in the gcc packages by default?

[ Parent | Reply to this comment ]

Posted by Steve (82.41.xx.xx) on Thu 10 Nov 2005 at 11:27
[ Send Message | View Steve's Scratchpad | View Weblogs ]

I'd like it to be - and will be suggesting this more strongly once Sarge is out.

For current reasons why it's not present please see the following bug reports:

Steve
-- Steve.org.uk

[ Parent | Reply to this comment ]

Posted by Anonymous (193.237.xx.xx) on Fri 14 Jan 2005 at 21:12
Thanks Steve,

I think this should have gone in a long time ago, and should be on by default. Debian runs the risk of being overtaken by Microsoft, which does enable such protection (if only in key applications) in XP SP2.

Of course the standard MS C++ compiler has offered it as an option for years, but no one at Microsoft thought to switch it on - sigh - problem of daft defaults. We should learn from MS experience, those who know what they are doing can always switch it off, but we should select defaults for people who don't understand and want things to "just work".

[ Parent | Reply to this comment ]

Sign In

Username:

Password:

[Register|Advanced]

 

Flattr

 

Current Poll

Which init system are you using in Debian?






( 1630 votes ~ 7 comments )

 

 

Related Links