As part of a course at university in computer security, I'm soon about to learn about buffer overflows and how to use them to as exploits. I'm trying to do some simple buffer overflow with the following code:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer_one[4], buffer_two[16];
strcpy(buffer_one, "one");
strcpy(buffer_two, "two");
strcpy(buffer_one, argv[1]);
printf("buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
printf("buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
}
I'm able to overwrite the content of buffer_one with the null terminator if i run
$./overflow 1234567890123456
buffer_two is at 0x7fff5fbff8d0 and contains '1234567890123456'
buffer_one is at 0x7fff5fbff8e0 and contains ''
But if i send more than 16 characters as argument, the program sends Abort trap. I supposed this is some sort of buffer protection on Snow Leopard (ASLR maybe?). If if make the size of buffer_two < 16, the adresse is still 16 bits apart
I'm running gcc -o overflow overflow.c -fno-stack-protector
to remove stack protection
Is there any solution to this problem, other than installing a VM running a linux dist.?
The key to why this is happening is the fact that
buffer_one
is located afterbuffer_two
in memory. This means that when you overflowbuffer_one
, you are not overflowing intobuffer_two
. Instead you are overflowing into stack memory being used to hold other things, such as the savedebp
pointer and most importantly, the return address.And this is exactly what you want to happen when attempting a buffer overflow exploit! When the program executes
strcpy(buffer_one, argv[1]);
the first four bytes fromargv[1]
go into the memory allocated forbuffer_one
. But then the next 12 start overflowing memory being used for other things, eventually overwriting the return address. Without seeing the machine code, I can't say for sure which bytes exactly are overflowing the return address. But I'm guessing the value of EIP at the time of the SIGABRT is 0x31323334 or something similar (hex representation of '1234'). The key is realizing that by being able to overwrite the return address, you control EIP. And when you control EIP, you control the system. (somewhat overexaggerated, but in most cases not far off) When you control EIP, you control which instructions the processor will execute next (putting aside for the moment the fact that the OS/kernel actually stand in between).Now if you find exactly which eight bytes overwrite the return address you can replace those bytes with the address of your buffer (
0x00007fff5fbff8e0
) and instead of returning to the original caller (libc in this case), the program will start executing the instructions you provided (AKA the shellcode). Note that you will have to fill in the implied 0s in the most significant places and provide the address as the actual nonprintable ASCII characters (0x00 0x00 0x7f 0xff 0x5f
and so on), not the actual digits/characters7ff5
etc. If using an x86-64 architecture, you'll also have to take the little-endianness into account and supply it backwards --0xe0 0xf8 0xbf
etc. Supplying these nonprintable characters is most easily accomplished using backticks and command substitution using a brief Python or Perl script:(The A's are padding to overflow the buffer.) Unfortunately, you won't be able to provide the 2 additional
\x00
needed for the address. One of these NULLs will be placed there for you bystrcpy
, but you'll have to get lucky with the last NULL and hope the address you're overwriting already started with0x00
(which is actually highly likely). Now when you execute this with the right number of A's, you'll probably still get a segmentation fault or even possibly illegal instruction, since you'll now jump to the capital A's and execute them as actual machine instructions (0x41
=>inc ecx
).Then finally the last piece is putting in the actual shellcode. Given your limited buffer sizes, it will be very hard to provide anything useful in only 12 bytes or so. Since you are writing the code in this case, the easiest thing will probably be to make your buffer bigger. If this weren't an option, then you could either A) use
buffer_two
as well for 16 more bytes since it comes beforebuffer_one
or B) provide the shellcode in an environment variable and jump to that instead.If you wish to write the actual shellcode yourself, you'll have to know how to perform syscalls and what calling conventions are and how to use them, as well as how to avoid NULL bytes in the shellcode. The other alternative is to use a payload generator such as the one included with Metasploit which will make it a lot easier (although you won't learn nearly as much).
These are technically the only pieces you need, especially since you have a good idea of what the address will be. However, many times (especially when the shellcode address is not known) a so-called NOP sled will be placed in front of the shellcode so that you don't have to get the address exactly right. A NOP sled (short for No Operation) is simply hundreds to thousands of NOP instructions (0x90) that you can jump into the middle of and then have no effect until execution continues into the shellcode.
If you trace everything in GDB and execution jumps to the shellcode correctly but you still get access violations, it's likely because the NX bit is set on the stack page, meaning that the processor will refuse to execute data on from the stack as instructions. I'm not sure if
execstack
is included with OSX or not, but if so, you can use it for testing purposes to disable the NX bit (execstack -s overflow
).I apologize for the wall of text, but I wasn't sure how far you wanted to go studying buffer overflows. There's other guides you can check out as well, such as Aleph One's archetypal guide, "Smashing the Stack for Fun and Profit". The Shellcoder's Handbook is a good book to check out as well, and I'm sure others can add recommendations.
TL;DR: In short, you are overflowing your buffer and overwriting saved pointers and return addresses with garbage.