what is a buffer?
In general, the term buffer is a temporary storage, a space in the memory used to store the data.
Memory Organization:
Stack: Contains arguments which are passed to the function and local variables.
Heap: Contains the dynamically allocated memory (malloc()).
Data:
– Initialized data segment: Contains the global, static and constant data.
– Uninitialized data segment (BSS): Contains uninitialized data.
Code or Text : Contains actual executable code or executable instructions.
More on Stack:
Stack is LIFO data structure, that is Last in First Out. Anything which is put into the stack last is the first which will have to be removed. Stack always grows down the memory address, from higher to lower and ESP points to the top of the stack.
Stack supports two operations:
PUSH: Pushing data into the stack. (After push fix ESP to top of the stack)
POP: Remove the data from the stack.(After remove fix ESP to top of the stack)
What is buffer overflow?
Buffer overflow occurs when the larger data is written to the buffer without checking the actual size of the buffer. It is due to an improper bound checking and results in overwriting the adjacent memory locations.
Stack overflow :
– Overwriting a local variable.
– Overwriting the return address.
– Overwriting a function pointer.
– Overwriting a parameter of a different stack frame or a nonlocal address.
Heap overflow :
Generally, heap overflow will happen in the heap area when allocating the memory dynamically using runtime memory allocation techniques.
More on Stack Based overflow:
Let’s consider some sample code which is vulnerable to stack based buffer overflow.
#include<stdio.h>
Input()
{
char buf[8];
gets(buf); /gets() itself is dangerous function because it does not do the bound check./
puts(buf);
}
main()
{
Input();
return 0;
}
In the above code gets(); is used which is going to get input from the user and writes into the variable ‘buf’ without checking the size of the variable ‘buf’. Suppose, if user supplies more than 8 bytes of data and gets() will still happily go ahead and write that onto the memory.
Run the Program :
Lets go ahead and run the the program with normal size of the buffer.
Here, we had given the input string “SecPod” which is less than the size of the buffer, so the program will print the string “SecPod” and exits normally.
Now, go ahead run the program with large data input.
Observe that the given string is “SecPod SecPod”, which is more than the size of the buffer available. So when executed, the program will throw “Segmentation fault (core dumped)”.
Let’s analyse the source code to understand further.
Vulnerable program stack layout:
Observations:
– Input() functions is not taking any argument, thus there are no arguments which are pushed into the stack.
– When main() program transfers control to the input() function, it will push the next instruction ie ‘return 0’ on to the stack.
– Once the execution goes to the input(), old value of the EBP will be stored and then local variables will be pushed into the stack.
– 8 bytes of buf variable is stored in the stack and 4 bytes of the old EBP is stored.
Now lets try it wirh GDB to get a better understanding.
Run the program with GDB:
- Compile the program with gdb.
gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -o stack_bof.o stack_bof.c
- Run the program with gdb.
gdb ./stack_bof.o
- List the source code in gdb.
– (gdb) list - Set the the two break points , first one at line 13 and second one at line 7
- (gdb) break 13
Breakpoint 1 at 0x8048425: file stack_bof.c, line 13.
(gdb) break 7
Breakpoint 2 at 0x804840a: file stack_bof.c, line 7.
(gdb)
Disassembled code:
Stack observation:
Overwritten return address:
Execute some crafted code:
Let’s slightly modify the original program code by adding a new function called shouldNotExecute() and compile the program again and run.
#include<stdio.h>
shouldNotExecute()
{
printf(“should not execute\n”);
}
Input()
{
char buf[8];
gets(buf); /gets() itself is dangerous function because it does not do the bound check./
puts(buf);
}
main()
{
Input();
return 0;
}
Run the above program
gcc -ggdb -fno-stack-protector -mpreferred-stack-boundary=2 -o stack_bof.o stack_bof.c
Load into the gdb at get the address of the function shouldNotExecute()
gdb ./stack_bof.o
Notice that shouldNotExecute() function is not called from anywhere. With special crafted inputs it is possible to execute the shouldNotExecute().
-AnTu