Notes on the creation of buffer overflow exploits
To overflow a buffer, we create an exploit string that will go in the buffer. The exploit code can be either at the beginning or at the end of this string. We will examine these two cases.
1) Exploit code at the beginning of the string
This is how the exploit string is laid out in the stack:
Lower Addresses ---------------------------------------- Exploit code | ---------------------------------------- | Buffer Padding to fill the rest of the buffer | ---------------------------------------- 4 bytes padding to overwrite EBP | EBP ---------------------------------------- 4 bytes to overwrite Return Address | with the address | Return Address of the beginning of the buffer | ---------------------------------------- Higher Addresses
We have to know the address of the beginning of the buffer. Because we have a little-endian system and because this address is at the very end of the exploit string, this address can start with a null byte. Thus, an address like 0x0064FD30 will not cause any problems, because the null byte will be at the very end of the exploit string.
We must be careful with the pushes that our exploit code does. Our exploit code resides in the buffer, which is in the stack. Each push also goes in the stack, in the buffer padding place. If the pushes are many, they will continue on to the exploit code region. If we have a lot of padding space, then we are OK. If not, we must change the ESP to point before our exploit code.
2) Exploit code at the end of the string
This is how the exploit string is laid out in the stack:
Lower Addresses ------------------------------------------ Padding to fill the whole buffer | Buffer ------------------------------------------ 4 bytes padding to overwrite EBP | EBP ------------------------------------------ 4 bytes to overwrite Return Address | with the address | Return Address of a "jmp esp" or "call esp" instruction | ------------------------------------------ Exploit code | After Return Address ------------------------------------------ Higher Addresses
The return address in this case must not have any null bytes. We do not need to know the address of the beginning of the buffer. In Windows 98, KERNEL32.DLL is loaded at the upper 2 GB region and has a “call esp” instruction at address 0xBFF79243. Thus we can use this address in all our exploits, without finding out the address of the overflowed buffer.
We do not have to be particularly careful with the pushes our code does. This is because the pushes will go to the buffer region which does not contain our exploit code. Thus, in this case, we do not fear about overwriting our exploit code with pushes.
Notes on testing buffer overflow exploits
For the following, it does not matter if the exploit code is either at the beginning of the overflowed buffer (and, thus, before the Return Address), or immediately after the Return Address.
When we debug a program, we may see unusual behaviour, such as the stack being cleared after each function return, or different behaviour, in general, as opposed to running the program without debugging it. Thus, the debugger is not to be trusted.
We must not use null bytes (0x00) in our shellcode, because our shellcode will usually fill a string. We can have only one null byte at the very end.
We must not use spaces in our shellcode, if our shellcode will be a command line argument.
1) When using command line arguments
If we want to overflow a buffer using a command line argument, we should create a program with the command to run in a character array as in:
“Victim.exe exploit”
and then use WinExec() to run that command.
We can also create (with a hex editor) a text file that contains the exploited command line argument and then copy and paste the file’s contents on “Project/Settings/Debug/Program arguments” in VC++.
In any case, the exploit code must not contain null characters or spaces.
The MS-DOS prompt, as in:
Victim.exe exploited-argument-hex-characters
may not pass the exploit’s non-printable characters correctly.
2) When using input from the user
If we want to overflow a buffer expecting input from the user, as in a gets command, then we should store the exploit in a text file (we will create the exploit text file with a hex editor) and use MS -DOS prompt with MS-DOS redirection, as in:
Victim.exe < exploit.txt
In this case, the exploit code must not contain null characters, but may contain spaces.
For a command line application, to just copy the exploit on the command line when the program stops and waits for user input may not pass to the exploited program the exploit’s non-printable characters correctly.