Create and test buffer overflow exploits

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.

Advertisements

About Dimitrios Kalemis

I am a systems engineer specializing in Microsoft products and technologies. I am also an author. Please visit my blog to see the blog posts I have written, the books I have written and the applications I have created. I definitely recommend my blog posts under the category "Management", all my books and all my applications. I believe that you will find them interesting and useful. I am in the process of writing more blog posts and books, so please visit my blog from time to time to see what I come up with next. I am also active on other sites; links to those you can find in the "About me" page of my blog.
This entry was posted in Security. Bookmark the permalink.