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-protector | enables 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-all | enables stack protection for all functions. |
-fno-stack-protector | disables 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:
- Reorders local variables so that buffers are placed after pointers
- Copies pointers in function arguments to an area before local buffers
- Omits instrumentation code from functions to reduce overhead
- 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.