Buffer/stack overflow is the most common type of exploitation method used by attackers for arbitrary code execution.
A buffer overflow condition exists when a program attempts to put more data in a buffer than it can hold or when a program attempts to put data in a memory area past a buffer. In this case, a buffer is a sequential section of memory allocated to contain anything from a character string to an array of integers. Writing outside the bounds of a block of allocated memory can corrupt data, crash the program, or cause the execution of malicious code. For more details refer wiki.
The most common mistake programmers do which cause buffer overflow is not checking/limiting user's input data size before it get stored in stack of the program. For example look at this program
#include <stdio.h>
int main(int argc, char **argv)
{
char buf[64];
strcpy(buf,argv[1]);
}
In the above program strcpy will copy all the data input as first argument in buf variable which has fix length of 64 bytes.
This will allow attacker to overflow the buffer space on stack of buf and change the return address which later makes controlling the eip causing arbitrary code execution.
As already mentioned, buffer overflow caused due to strcpy is the most common type of exploitation method, hence is mostly get noticed and fixed. While code auditing, there is very less amount of chances that you can find these type of overflow vulnerability. Usually programmers fix this type of error by replacing strcpy with strncpy(which copy fix amount of byte into buffer) or do pre-checking of number of byte entered by user. But even in this case there is probability that programmer can do off-by-one error which cause due to moving the size of data put on buffer one byte more then the size of data. It can cause due to either misinterpretation of conditions value or being not aware of fact if your string provided is not null terminating then strcpy will add a null terminator at the end of string, makes the size of string increase by one byte in stack. To understand more lets look at this simple example.
#include <stdio.h>
void func(char *str)
{
char buf[256];
int i;
for (i=0;i<=256;i++)
buf[i] = str[i];
}
int main(int argc, char **argv)
{
func(argv[1]);
}
In this program the programmer has put i=<256
instead of i<256
which will cause the overflow of buf and replacing the least significant byte of ebp.
You must be thinking that changing the least significant digit doesn't harm in any way since still we have no control over eip. But that's not true!
Understanding the cause of trouble
Once we have corrupted the ebp of the program by overflow, the instruction run at the end is func+92 leave
instruction.
leave
is equivalent to <mov esp,ebp; pop corrupted_ebp>
Now our corrupted value is in ebp register.
Program will now return to main function.
When leave of main+26 runs, will cause this
<mov esp,corrupted_ebp>. So now our corrupted ebp is moved to esp which change the top of stack to arbitrary place and then ret will pop eip cause eip
to point at arbitrary address.
Lets see it in action
First load the program inside gdb.
Lets first break at func+92
Now run the program with 250 'A's as first argument and look what is original ebp is
Lets run the program with 256 'A's and then check the new ebp
.
So the ebp
has changed to 0xbffff500 from 0xbffff545.
Now look for the address of our buf variable.
Our buf starts at 0xbffff4b0 and ends at 0xbffff5b8 after which is our ebp
.
Now put a breakpoint at main+27 and continue the program.
So x/x $esp shows that top of our stack is changed to 0xbffff504 and hence 0xbffff508 will get poped to eip
.
So our task is to put the address of our shellcode at 0xbffff508.
The shellcode I am using is of 28 bytes.
#!/usr/bin/perl
print "\x31\xc0\x50\x68\x2f\x2f\x73";
print "\x68\x68\x2f\x62\x69\x6e\x89";
print "\xe3\x89\xc1\x89\xc2\xb0\x0b";
print "\xcd\x80\x31\xc0\x40\xcd\x80";
At final the buf memory will look like this
Here, after shellcode we will repeatedly put the address of starting of our shellcode 0xbffff4bc to make it much easy to jump to our shellcode even if the esp
after main change slightly.
You can also put liitle `nop` sledge before shellcode for better success rate.
Lets run this program with our shellcode file as argument.
Awesome!!! we got the shell inside gdb.
To run it directly into your shell you must have aslr disabled.
$echo 0 > sudo /proc/sys/kernel/randomize_va_space
Even after that it may not work because the addresses on shell may little different then on gdb. So best bet is to put nop sledge before shellcode to make it work correctly.
So, we have learned to successfully exploited off-by-one Vulnerability present in a program.
Off-by-one vulnerability is little difficult to find and hence can be present in big softwares also. Finding it gives you better credits as compare to other vulnerability since it is easily exploitable. But there are aslr and no execution of stack mitigation present to stop these.