Stack Smashing at Home

There are various wargaming sites such as SmashTheStack, OverTheWire, and IO Wargame that provide a platform for users to legally exploit real world software vulnerabilities. Source code is provided with a few of the challenges, which you can copy to a local research machine instead of working remotely. Keep in mind that the sites hosting these challenges have disabled most if not all of the compiler and kernel protections, so when you compile the source code at home you may have unintended results.

The following is a short guide to help you disable most of the protections that will make it easier to exploit these challenges at home.

WARNING: Use a virtual machine when compiling untrusted software.

I created a sample C program so you can follow along. Copy & paste the code below into an editor then save the file as: app.c

 // app.c
 #include 
 #include 
 
 int
 main(void)
 {
   int a = 1;
   char buf[4];
   char* ptr;
 
   printf("Stack Smashing @ Home\n");
   strcpy(buf, "rehsams");
 
   return(0);
 }

Use the commands below to compile the code. We are simulating standalone code you retrieved from one of the challenge sites. You will want to note if it is on a 32 bit or 64 bit system.

mike@quazi(/tmp):$ gcc -o app app.c


GCC Protections

Stack Smashing Protection (SSP) / ProPolice

-fstack-protectorenables stack protection for functions using character arrays.
-fstack-protector-strong(gcc >=4.9) – builds on fstack-protector by including protection for functions that have local array definitions, or have references to local frame addresses.
-fstack-protector-allenables stack protection for all functions.
-fno-stack-protectordisables stack protection.

SSP is included in gcc >=4.1 but not enabled, however it is distribution specific. To check if it is enabled type the following:

mike@quazi(/tmp):$ objdump -d app | grep stack_chk_fail
080482e8 <__stack_chk_fail@plt>:
 80483f8:       e8 eb fe ff ff          call   80482e8 <__stack_chk_fail@plt>

What it does:

  1. Reorders local variables so that buffers are placed after pointers
  2. Copies pointers in function arguments to an area before local buffers
  3. Omits instrumentation code from functions to reduce overhead
  4. Adds a call to __stack_chk_fail() in the epilogue

GNU_STACK ELF markings

-z execstack – enable executable stack
-z noexecstack – disable executable stack (default)

gcc adds the stack marking above when you compile your source code. The default behavior is that your executable ends up with a non-executable stack unless there was some indication that an executable stack was necessary (trampolines).

First we compile a test program and check for the stack marking:

mike@quazi(/tmp):$ gcc -o app app.c && readelf -l app | grep -A2 -i stack
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

Notice by default on my test system the stack is Read & Write (RW).

mike@quazi(/tmp):$ gcc -z execstack -o app app.c && readelf -l app | grep -A2 -i stack
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4

We compiled the test program using ‘execstack’ and you can see we now have Read, Write, and Execute (RWE). There is also a execstack(8) tool included in the prelink package that can dynamically set or unset the executable stack.

mike@quazi(/tmp):$ readelf -l app | grep -A2 -i stack
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4

Use the readelf(1) tool to verify we have an executable stack and then remove the executable flag using the syntax below:

mike@quazi(/tmp):$ execstack -c app && readelf -l app | grep -A2 -i stack
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

You can also reset the executable flag by using the ‘-s’ option with execstack:

mike@quazi(/tmp):$ execstack -s app && readelf -l app | grep -A2 -i stack
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4

*** Note: if the GNU_STACK marking is missing then the stack is likely executable ***

Buffer & Format String Vulnerability Checks /  -D_FORTIFY_SOURCE

Fortify source protection should be disabled by default, but if you have a distro that does something different you can disable it with:

mike@quazi(/tmp):$ gcc -O1 -D_FORTIFY_SOURCE=0 -o app app.c

If a binary is compiled with -D_FORTIFY_SOURCE=n
  n = 2 / format strings (%n) and buffer checks
   n = 1 / buffer overflow checks only

When we try compiling using the buffer overflow checks only we get a warning because in the sample code we are copying a string larger than the size of the character array to simulate an overflow.

mike@quazi(/tmp):$ gcc -O1 -D_FORTIFY_SOURCE=2 -o app app.c
app.c: In function ‘main’:
app.c:12: warning: call to __builtin___strcpy_chk will always overflow destination buffer

Now run the application and watch the output:

mike@quazi(/tmp):$ ./app
Stack Smashing @ Home
 
*** buffer overflow detected ***: ./app terminated
======= Backtrace: =========
/lib/libc.so.6(__chk_fail+0x44)[0xb7f56944]
/lib/libc.so.6(__strcpy_chk+0x3d)[0xb7f55e3d]
./app[0x80483e2]
/lib/libc.so.6(__libc_start_main+0xd8)[0xb7e87df8]
./app[0x8048331]
======= Memory map: ========
08048000-08049000 r-xp 00000000 03:01 6045744    /tmp/app
08049000-0804a000 rw-p 00000000 03:01 6045744    /tmp/app
0804a000-0806b000 rw-p 0804a000 00:00 0          [heap]
b7e71000-b7e72000 rw-p b7e71000 00:00 0
b7e72000-b7fae000 r-xp 00000000 03:01 2916420    /lib/libc-2.5.so
b7fae000-b7faf000 r--p 0013c000 03:01 2916420    /lib/libc-2.5.so
b7faf000-b7fb1000 rw-p 0013d000 03:01 2916420    /lib/libc-2.5.so
b7fb1000-b7fb4000 rw-p b7fb1000 00:00 0
b7fc4000-b7fce000 r-xp 00000000 03:01 4784221    /usr/lib/libgcc_s.so.1
b7fce000-b7fcf000 rw-p 00009000 03:01 4784221    /usr/lib/libgcc_s.so.1
b7fcf000-b7fd1000 rw-p b7fcf000 00:00 0
b7fd1000-b7fec000 r-xp 00000000 03:01 2916462    /lib/ld-2.5.so
b7fec000-b7fee000 rw-p 0001b000 03:01 2916462    /lib/ld-2.5.so
bfb91000-bfba6000 rw-p bfb91000 00:00 0          [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0          [vdso]
Aborted

To check for the FORTIFY_SOURCE markings type the following commands:

mike@quazi(/tmp):$ objdump -d app | grep _chk
080482e4 <__strcpy_chk@plt>:
 80483dd:       e8 02 ff ff ff          call   80482e4 <__strcpy_chk@plt>


Kernel Level Protections

Address Space Layout Randomization (ASLR)

ASLR is in vanilla linux kernels >= 2.6    to disable it type the following in a terminal:

root@box# echo 0 > /proc/sys/kernel/randomize_va_space

or if you prefer:

root@box# /sbin/sysctl -w kernel.randomize_va_space=0


and if you only want ASLR disabled for each invocation use setarch(8):

mike@quazi(/tmp):$ /usr/sbin/setarch i686 -R ./app

Exec-Shield (32-bit x86 CPUs)

In order to disable exec-shield:

root@box# echo 0 > /proc/sys/kernel/exec-shield
root@box# echo 0 > /proc/sys/kernel/exec-shield-randomize

or if you prefer:

root@box# /sbin/sysctl -w kernel.exec-shield=0
root@box# /sbin/sysctl -w kernel.exec-shield-randomize=0

*** Note: if you want to maintain the sysctl(8) changes after a reboot add them to sysctl.conf ***

 Security-Enhanced Linux (SELinux)

If you are using a distribution that supports selinux you can disable it using the following commands:

root@box# echo 0 >/selinux/enforce

Or you can make use of setenforce(8):

root@box# setenforce Permissive

To permanently disable selinux edit: /etc/sysconfig/selinux or /etc/selinux/config and modify the contents so that the following line is present:

SELINUX=disabled


CPU Architecture

Pay attention to the CPU architecture (32 bit vs 64 bit) and make sure your local machine and the challenge box are the same.

That should get you going. If I missed something feel free to point it out. Thanks for reading. 

 

Start a discussion or ask a question.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d