题外话
不得不说,现在的AI能力真的吓人,大部分题目解题都是分析出思路,慢慢调教AI,最终解出flag,前两道题直接粘汇编代码AI秒出答案。最后这两道题就很离谱,用的gpt5.3-codex掉进了很多坑,都是一步一步慢慢调教爬出来,海量token尝试后的结果。以下内容是AI总结,大家看个热闹。
【春节】解题领红包之十 {Windows 高级题} 出题老师:Poner
0x00 前言
本题的关键不是“改比较常量”或“硬改全局变量”,而是还原真实校验链。
最终结论:UID 对应的正确输入 flag 是一段 128 位 hex 字符串(64 字节),不是 0x373f37f2。
0x01 结论先行
UID=551842 的正确 flag:
1da343dd7ce595876e7af7a5bbd885ed8003ad427e8cfcaa615bb85b00143b52369df87be613639a293536bc6340e798a8e503fe9e6d7eeaa6c799f5561bdc5b
0x02 误区澄清
很多分析会盯住这个点:
0x14001f746: cmp r8d, 0x373f37f2
这只是最终逻辑中的一个常量比较点,不是用户直接输入值。
把 0x373f37f2 直接当 flag(十进制/十六进制字符串)会失败。
0x03 Verify 按钮事件与分支条件(静态)
核心状态机在:
0x140004df0(seg_4df0_5f00_annot.txt)
关键调用点:
0x599a: call [0x14014c6d8] -> 实际指向 0x1400cd490
0x5a05: call [0x14014c6f8] -> 实际指向 0x1400cd490
与校验结果直接相关的全局变量:
g420 = [0x142632420]
g424 = [0x142632424]
关键写点:
- 失败路径:
0x5bf0/0x5bfa、0x5c3d/0x5c47 写成 g420=4, g424=0
- 成功路径:
0x5c63/0x5c72、0x5cc0/0x5ccf 写成 g420=3, g424=al
也就是:cd490 链返回为真时,al=1 会被写入 g424,之后走成功消息路径。
0x04 真正的校验链(静态)
从 0x1400cd490 开始,主链路为:
cd490 -> cf090
cf090 -> cf270
cf270 通过后 cd490 -> cf910
cf910 -> fd790
cf910 -> d3b20
cf910 -> cfb10
对应关键地址:
cd490:0x1400cd490
cf090:0x1400cf090
cf270:0x1400cf270
cf910:0x1400cf910
fd790:0x1400fd790
d3b20:0x1400d3b20
cfb10:0x1400cfb10
4.1 cf090:输入 flag 的 hex 解码
在 cf090 内可见对字符范围判断与 nibble 拼接:
- 支持
0-9, a-f, A-F
- 两个 hex 字符解一个字节
结论:用户输入应是 hex 字符串。
4.2 cf270:长度约束
在 cf270 中可见:
cmp rsi, 0x40
sete byte ptr [rsp + 0x23]
结论:解码后长度必须是 0x40(64 字节)。
4.3 cf910:调用 fd790 生成期望数据,再调用 d3b20
关键调用点:
0x1400cfa6e: call 0x1400fd790
0x1400cfa85: call 0x1400d3b20
- 调用参数:
rcx = user_bytes_ptr, rdx = expect_buf_ptr, r8d = 0x40
4.4 d3b20:比较核心与返回值
在 d3b20 中可见逐字节处理块(如 0x44a0 / 0x5253)以及尾部返回判定:
0x1400d5a33: cmp byte ptr [rsp+7], 0
0x1400d5a38: sete byte ptr [rsp+0xf]
0x1400d5b23: movzx eax, byte ptr [rsp+0xf]
结论:[rsp+7] == 0 时返回 1,即比较通过。
0x05 动态取证(最小 hook)
为避免重侵入,仅 hook:
d3b20 入口/返回
0x1f746 比较点
0x55f00 验证结果点
Stage 1:输入 00*64
在 d3b20 入口抓到:
user_bytes = 我们输入解码后的 64 字节(全 0)
expect_bytes = 程序运行时生成的 64 字节(关键)
d3_ret = 0
verify_result = 0
Stage 2:把 expect_bytes 原样作为输入 flag
再次调用后:
d3_ret = 1
verify_result = 1
g420=3, g424=1(见 callprep_probe.log)
即证实该 expect_bytes 正是 UID 对应正确 flag 的还原结果。
0x06 脚本实现
6.1 solve_uid551842.py(核心取证脚本)
import ctypes
import json
import os
import subprocess
import time
from ctypes import wintypes
from pathlib import Path
import frida
TARGET = str(Path("Q10_upxdec.exe").resolve())
LOG = Path("solve_uid551842.log")
user32 = ctypes.WinDLL("user32", use_last_error=True)
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, wintypes.LPARAM)
def log(obj):
with LOG.open("a", encoding="utf-8") as f:
f.write(json.dumps(obj, ensure_ascii=False) + "\n")
def enum_windows(pid: int):
out = []
@EnumWindowsProc
def cb(hwnd, _):
p = wintypes.DWORD()
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(p))
if p.value == pid and user32.IsWindowVisible(hwnd):
cls = ctypes.create_unicode_buffer(256)
user32.GetClassNameW(hwnd, cls, 256)
title_len = user32.GetWindowTextLengthW(hwnd)
title = ctypes.create_unicode_buffer(title_len + 1)
user32.GetWindowTextW(hwnd, title, title_len + 1)
out.append((hwnd, cls.value, title.value))
return True
user32.EnumWindows(cb, 0)
return out
def find_main_window(pid: int):
for hwnd, cls, _ in enum_windows(pid):
if cls == "52PoJie_CrackMe_2026":
return hwnd
ws = enum_windows(pid)
return ws[0][0] if ws else None
JS = r"""
const base = Process.enumerateModules()[0].base;
const prep = new NativeFunction(base.add(0x60770), "uint8", ["pointer", "pointer", "pointer"]);
let seq = 0;
let captured = false;
function emit(o, data) {
o.seq = ++seq;
send(o, data);
}
function readHex(ptr, n) {
let s = "";
for (let i = 0; i < n; i++) {
const v = ptr.add(i).readU8();
s += ("0" + v.toString(16)).slice(-2);
}
return s;
}
Interceptor.attach(base.add(0x00d3b20), {
onEnter() {
const n = this.context.r8.toUInt32();
if (n === 0x40 && !captured) {
captured = true;
try {
emit({ type: "user_bytes", n: n, hex: readHex(this.context.rcx, n) });
} catch (e) {
emit({ type: "user_bytes_err", err: String(e) });
}
try {
emit({ type: "expect_bytes", n: n, hex: readHex(this.context.rdx, n) });
} catch (e) {
emit({ type: "expect_bytes_err", err: String(e) });
}
}
},
onLeave(ret) {
emit({ type: "d3_ret", al: ret.toUInt32() & 0xff });
}
});
Interceptor.attach(base.add(0x001f746), {
onEnter() {
emit({ type: "cmp", r8d: this.context.r8.toUInt32() });
}
});
Interceptor.attach(base.add(0x0055f00), {
onEnter() {
emit({ type: "verify_result", edx: this.context.rdx.toUInt32() });
}
});
rpc.exports = {
run(hwnd, uid, flag) {
const pu = Memory.allocUtf16String(uid);
const pf = Memory.allocUtf16String(flag);
return prep(ptr(hwnd), pu, pf);
}
};
"""
def probe_once(uid: str, flag_hex: str, wait_s: float = 2.0):
proc = subprocess.Popen([TARGET])
pid = proc.pid
log({"type": "probe_start", "pid": pid, "uid": uid, "flag_len": len(flag_hex)})
sess = None
events = []
out = {
"uid": uid,
"flag_hex": flag_hex,
"expect_hex": None,
"user_hex": None,
"cmp_values": [],
"verify_values": [],
"d3_ret": [],
}
try:
hwnd = None
for _ in range(100):
hwnd = find_main_window(pid)
if hwnd:
break
time.sleep(0.05)
if not hwnd:
raise RuntimeError("main window not found")
log({"type": "probe_hwnd", "pid": pid, "hwnd": int(hwnd)})
sess = frida.attach(pid)
script = sess.create_script(JS)
def on_message(msg, data):
if msg.get("type") != "send":
events.append({"raw": msg})
return
payload = msg.get("payload", {})
events.append(payload)
t = payload.get("type")
if t in ("expect_bytes", "user_bytes"):
hx = payload.get("hex")
if isinstance(hx, str) and hx:
if t == "expect_bytes":
out["expect_hex"] = hx
else:
out["user_hex"] = hx
elif t == "cmp":
out["cmp_values"].append(int(payload.get("r8d", 0)))
elif t == "verify_result":
out["verify_values"].append(int(payload.get("edx", 0)))
elif t == "d3_ret":
out["d3_ret"].append(int(payload.get("al", 0)))
script.on("message", on_message)
script.load()
log({"type": "probe_frida_loaded", "pid": pid})
log({"type": "probe_run_enter", "pid": pid})
ret = int(script.exports_sync.run(int(hwnd), uid, flag_hex))
log({"type": "probe_run_leave", "pid": pid, "ret": ret})
out["prep_ret"] = ret
deadline = time.time() + wait_s
while time.time() < deadline:
if out["verify_values"] and out["expect_hex"] is not None:
break
time.sleep(0.05)
finally:
try:
proc.kill()
except Exception:
pass
out["events"] = events
log(
{
"type": "probe_done",
"pid": pid,
"expect_captured": out["expect_hex"] is not None,
"verify_values": out["verify_values"],
"d3_ret": out["d3_ret"],
"event_types": [e.get("type", "raw") for e in events if isinstance(e, dict)],
"errors": [e for e in events if isinstance(e, dict) and str(e.get("type", "")).endswith("_err")],
}
)
return out
def solve_uid(uid: str):
# Stage 1: trigger real pipeline once; grab the 64-byte expected buffer from cf910->d3b20 call site.
s1 = probe_once(uid, "00" * 64, wait_s=2.5)
if not s1.get("expect_hex"):
raise RuntimeError("failed to capture expected bytes from runtime")
flag_hex = s1["expect_hex"]
# Stage 2: feed captured expected bytes back as input and verify pass.
s2 = probe_once(uid, flag_hex, wait_s=2.5)
passed = any(v == 1 for v in s2.get("verify_values", []))
return flag_hex, passed, s1, s2
def main():
uid = "551842"
if LOG.exists():
LOG.unlink()
log({"type": "start", "uid": uid, "target": TARGET})
flag_hex, passed, s1, s2 = solve_uid(uid)
log({"type": "stage1", "expect_hex": s1.get("expect_hex"), "d3_ret": s1.get("d3_ret"), "verify_values": s1.get("verify_values")})
log({"type": "stage2", "flag_hex": flag_hex, "d3_ret": s2.get("d3_ret"), "verify_values": s2.get("verify_values"), "passed": passed})
print(flag_hex, flush=True)
print("PASS" if passed else "FAIL", flush=True)
os._exit(0)
if __name__ == "__main__":
main()
6.2 gen_flag_uid551842.py(硬编码答案)
#!/usr/bin/env python3
"""
UID 551842 correct flag (128 hex chars, 64 bytes).
This is not the cmp immediate at 0x14001f746.
It is the runtime expected 64-byte buffer generated in the cd490->cf910->fd790 chain,
which d3b20 compares against the user-provided decoded bytes.
"""
UID551842_FLAG_HEX = (
"1da343dd7ce595876e7af7a5bbd885ed"
"8003ad427e8cfcaa615bb85b00143b52"
"369df87be613639a293536bc6340e798"
"a8e503fe9e6d7eeaa6c799f5561bdc5b"
)
def flag_for_uid(uid: int) -> str:
if uid != 551842:
raise ValueError("This helper is for UID 551842 only.")
return UID551842_FLAG_HEX
def main() -> None:
print(flag_for_uid(551842))
if __name__ == "__main__":
main()
0x07 最终答案
0x07 关键函数反汇编分析
7.1 cf090:输入 flag 的 hex 解码函数
0x00000001400cf090: push r15
0x00000001400cf092: push r14
0x00000001400cf094: push r13
0x00000001400cf096: push r12
0x00000001400cf098: push rsi
0x00000001400cf099: push rdi
0x00000001400cf09a: push rbp
0x00000001400cf09b: push rbx
0x00000001400cf09c: sub rsp, 0x28
0x00000001400cf0a0: mov rdi, qword ptr [rdx + 8]
0x00000001400cf0a4: cmp rdi, 2
0x00000001400cf0a8: mov qword ptr [rsp + 0x20], rcx
0x00000001400cf0ad: jae 0x1400cf0bb
...
0x00000001400cf124: cmp bpl, 0xa
0x00000001400cf128: jb 0x1400cf193
0x00000001400cf12a: lea r8d, [rdx - 0x61]
0x00000001400cf12e: cmp r8b, 5
0x00000001400cf132: ja 0x1400cf180
0x00000001400cf134: add dl, 0xa9
...
关键逻辑说明:
- 支持字符范围:0-9, a-f, A-F
- 两个 hex 字符解一个字节
- 将用户输入的 hex 字符串解码为二进制字节
7.2 cf270:长度约束函数
0x00000001400cf270: push r14
0x00000001400cf272: push rsi
0x00000001400cf273: push rdi
0x00000001400cf274: push rbp
0x00000001400cf275: push rbx
0x00000001400cf276: sub rsp, 0x30
...
0x00000001400cf7a0: cmp rsi, 0x40
0x00000001400cf7a4: mov dword ptr [rsp + 0x24], 0x7e1d
0x00000001400cf7ac: sete byte ptr [rsp + 0x23]
...
关键逻辑说明:
- 检查解码后长度是否为 0x40(64 字节)
- 通过
cmp rsi, 0x40 和 sete 指令实现长度验证
7.3 cf910:主校验调度函数
0x00000001400cf910: push rsi
0x00000001400cf911: push rdi
0x00000001400cf912: push rbx
0x00000001400cf913: sub rsp, 0x230
...
0x00000001400cfa6e: call 0x1400fd790 ; 生成期望数据
0x00000001400cfa73: test al, al
0x00000001400cfa75: je 0x1400cfaa0
0x00000001400cfa77: mov rcx, qword ptr [rsi]
0x00000001400cfa7a: lea rdx, [rsp + 0x30]
0x00000001400cfa7f: mov r8d, 0x40
0x00000001400cfa85: call 0x1400d3b20 ; 比较用户数据和期望数据
...
关键逻辑说明:
- 调用
fd790 生成期望的 64 字节数据
- 调用
d3b20 比较用户输入的解码数据和期望数据
- 参数:
rcx = user_bytes_ptr, rdx = expect_buf_ptr, r8d = 0x40
7.4 d3b20:逐字节比较核心函数
0x00000001400d3b20: push r15
0x00000001400d3b22: push r14
0x00000001400d3b24: push r13
0x00000001400d3b26: push r12
0x00000001400d3b28: push rsi
0x00000001400d3b29: push rdi
0x00000001400d3b2a: push rbp
0x00000001400d3b2b: push rbx
0x00000001400d3b2c: sub rsp, 0x48
...
0x00000001400d5a33: cmp byte ptr [rsp+7], 0
0x00000001400d5a38: sete byte ptr [rsp+0xf]
0x00000001400d5a3b: movzx eax, byte ptr [rsp+0xf]
...
关键逻辑说明:
- 逐字节处理用户输入和期望数据
- 尾部返回判定:
[rsp+7] == 0 时返回 1(比较通过)
- 包含大量混淆的算术运算和条件跳转
7.5 fd790:期望数据生成函数
0x00000001400fd790: push r15
0x00000001400fd792: push r14
0x00000001400fd794: push r13
0x00000001400fd796: push r12
0x00000001400fd798: push rsi
0x00000001400fd799: push rdi
0x00000001400fd79a: push rbp
0x00000001400fd79b: push rbx
0x00000001400fd79c: sub rsp, 0x398
...
关键逻辑说明:
- 根据 UID 和内部状态生成 64 字节的期望数据
- 包含大量混淆的算术运算和控制流
- 是整个校验链中最核心的生成函数
7.6 cfb10:辅助处理函数
0x00000001400cfb10: push r15
0x00000001400cfb12: push r14
0x00000001400cfb14: push r13
0x00000001400cfb16: push r12
0x00000001400cfb18: push rsi
0x00000001400cfb19: push rdi
0x00000001400cfb1a: push rbp
0x00000001400cfb1b: push rbx
0x00000001400cfb1c: sub rsp, 0x38
...
关键逻辑说明:
- 在 d3b20 比较失败时被调用
- 用于处理失败后的状态
- 同样包含大量混淆逻辑
0x08 总结
0x373f37f2 不是直接 flag。
- 正解是还原
cd490 -> cf090 -> cf270 -> cf910 -> fd790 -> d3b20 全链路。
- 本题本质是“UID 参与生成期望 64 字节,再与用户 hex 解码数据比较”。
【春节】解题领红包之十一 {MCP 中级题} 出题老师:正己
最终结果
flag{new_year_2026_keep_warm}
关键思路
- 先从公开文档拿口令线索:
玄霄 + 密令 -> ctf_request=玄霄密令。
- 按新增提示走复核链路:先触发拒绝获取最新
audit_log_id,再用 review_document 提交复核三字段。
review_document 会下发 tool_access_token。
- 用
tool_access_token 直接调用 get_audit_log,拿到 _audit_token。
- 用
_audit_token 读取 secret:/board/resolutions,拿到 flag。
文档内容
1. public:/docs/annual_report
- id:
public:/docs/annual_report
- title:
云台岁终告谕·新岁启卷
- department:
云台司
- access_level:
1
- tags:
公开, 告谕, 真卷
- content:
云台司岁终告谕:凡尘与灵脉同庆,诸峰钟鼓齐鸣。请诸弟子于新岁晨钟后赴山门广场,恭听掌门示诫。偈语首字:玄霄。
2. public:/docs/employee_handbook
- id:
public:/docs/employee_handbook
- title:
门规行仪录
- department:
功德堂
- access_level:
1
- tags:
公开, 门规, 真卷
- content:
行走尘寰,当守门规:不得擅离山门,不得窥探禁阁。新岁值守次序已悬于功德堂。偈语末字:密令。
3. public:/docs/challenge_hint
- id:
public:/docs/challenge_hint
- title:
玄堂复核提示
- department:
玄堂
- access_level:
1
- tags:
公开, 提示, 复核
- content:
复核字段: ctf_request=<passphrase> audit_log_id=<audit_log_id> seal=<seal> seal 公式:sha256(audit_log_id + passphrase),取前8位小写十六进制。
4. public:/docs/board_route_hint
- id:
public:/docs/board_route_hint
- title:
议会卷宗路引
- department:
玄堂
- access_level:
1
- tags:
公开, 提示, 路引
- content:
终卷路径由三段拼接:前缀 secret:/,域 board,条目 resolutions。按此组合即可得到最终文档 URI。
- id:
public:/docs/false_scroll
- title:
伪卷疑章
- department:
杂录坊
- access_level:
1
- tags:
公开, 疑卷
- content:
残页妄载:偈语首字作“玄冥”,末字作“密符”。此卷断简残缺,疑点甚多,务必慎辨真伪。
测试信息
- 测试时间:本轮会话
- 会话参数:
session_id = sess_769f8128-582d-4460-8994-6256ee7bc079
- 测试方法:对每个工具分别调用一次
update_tool_chain(session_id, chain_config=[<tool_name>])
综合分析表
| 工具名 |
调用方法 |
必需参数 |
可选参数 |
用途 |
加入链 |
start_session |
mcp_remote52pojie_start_session(user_id, role) |
user_id, role |
- |
获取 session_id,这是调用其他工具的第一步 |
❌ |
search_documents |
mcp_remote52pojie_search_documents(session_id, query) |
session_id, query |
- |
在文档库中搜索相关文档 |
✅ |
list_documents |
mcp_remote52pojie_list_documents(session_id, filter?) |
session_id |
filter |
获取当前会话可访问的文档列表 |
✅ |
list_all_documents |
mcp_remote52pojie_list_all_documents(session_id, access_token?) |
session_id |
access_token |
列出所有文档,包括受限文档 |
❌ |
get_document |
mcp_remote52pojie_get_document(session_id, doc_id, access_token?) |
session_id, doc_id |
access_token |
读取指定文档的详细内容 |
✅ |
review_document |
mcp_remote52pojie_review_document(session_id, doc_id, access_token?, review_notes?) |
session_id, doc_id |
access_token, review_notes |
对文档进行审阅并提交意见 |
❌ |
log_sensitive_access |
mcp_remote52pojie_log_sensitive_access(session_id, doc_id, access_token?, reason?) |
session_id, doc_id |
access_token, reason |
记录敏感文档的访问日志 |
❌ |
get_audit_log |
mcp_remote52pojie_get_audit_log(session_id, log_id, access_token?) |
session_id, log_id |
access_token |
查看审计日志详情 |
❌ |
get_system_config |
mcp_remote52pojie_get_system_config(session_id, access_token?, config_key?) |
session_id |
access_token, config_key |
获取系统配置信息 |
❌ |
update_tool_chain |
mcp_remote52pojie_update_tool_chain(session_id, chain_config, access_token?, audit_log_id?) |
session_id, chain_config |
access_token, audit_log_id |
更新工具链配置 |
❌ |
execute_tool_chain |
mcp_remote52pojie_execute_tool_chain(session_id, chain_id?, parameters?) |
session_id |
chain_id, parameters |
执行预定义的工具链 |
❌ |
结果归纳
- 当前普通会话下,
update_tool_chain 可直接配置的工具只有:
search_documents ✅
list_documents ✅
get_document ✅
get_audit_log 不是简单白名单拒绝,而是返回 ctf_token_required,说明它走的是“令牌门槛”而非纯禁用。
- 其他工具统一表现为
invalid_chain,属于链配置白名单外工具。
对 access_token/ctf_token 的含义补充
- 从公开文档提示可知:
seal = sha256(audit_log_id + passphrase),取前 8 位小写十六进制。
- 结合本次
get_audit_log -> ctf_token_required,可推测敏感链能力需要一个与 audit_log_id 绑定的动态令牌(CTF token)。
调用参数说明
execute_tool_chain:必需 session_id;可选 chain_id(不传时走默认链)。
记录表(仅保留返回含 results 的调用)
| 记录 |
session_id |
输入 chain_id |
返回 chain |
工具 |
工具必需参数 |
该步返回 |
| A-1 |
sess_769f8128-582d-4460-8994-6256ee7bc079 |
admin |
admin |
list_all_documents |
session_id |
permission_denied (insufficient_privileges) |
| A-2 |
sess_769f8128-582d-4460-8994-6256ee7bc079 |
admin |
admin |
get_document |
session_id, doc_id |
document_not_found |
| A-3 |
sess_769f8128-582d-4460-8994-6256ee7bc079 |
admin |
admin |
get_system_config |
session_id |
permission_denied (insufficient_privileges) |
| B-1 |
sess_2449ad67-9b20-410b-b553-6d85125a007d |
(不传) |
session |
list_documents |
session_id |
成功返回 4 条公开文档 |
| B-2 |
sess_2449ad67-9b20-410b-b553-6d85125a007d |
(不传) |
session |
get_document |
session_id, doc_id |
document_not_found |
| B-3 |
sess_2449ad67-9b20-410b-b553-6d85125a007d |
(不传) |
session |
search_documents |
session_id, query |
query_required |
| C-1 |
sess_769f8128-582d-4460-8994-6256ee7bc079 |
audit |
audit |
review_document |
session_id, doc_id |
document_not_found |
| C-2 |
sess_769f8128-582d-4460-8994-6256ee7bc079 |
audit |
audit |
log_sensitive_access |
session_id, doc_id |
permission_denied (insufficient_privileges) |
核心线索提取
实战步骤(成功记录)
步骤 1: 创建会话
# 调用 MCP 工具创建会话
mcp_remote52pojie_start_session(user_id="551842")
返回结果:
{
"session_id": "sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
"user_level": 1,
"role": "employee"
}
步骤 2: 读取公开提示文档
# 依次读取 4 个关键公开文档
docs = [
"public:/docs/annual_report",
"public:/docs/employee_handbook",
"public:/docs/challenge_hint",
"public:/docs/board_route_hint"
]
for doc_id in docs:
mcp_remote52pojie_get_document(
session_id="sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
doc_id=doc_id
)
步骤 3: 提取线索并计算 passphrase
# 从 annual_report 和 employee_handbook 提取线索
first_part = "玄霄" # 偈语首字
second_part = "密令" # 偈语末字
passphrase = first_part + second_part # "玄霄密令"
步骤 4: 触发拒绝,获取 audit_log_id
# 尝试读取终卷(会被拒绝,但返回 audit_log_id)
try:
mcp_remote52pojie_get_document(
session_id="sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
doc_id="secret:/board/resolutions"
)
except Exception as e:
# 从错误响应中提取 audit_log_id
audit_log_id = "audit_1772291097099_7"
拒绝响应示例:
{
"error": "access_denied",
"audit_log_id": "audit_1772291097099_7"
}
步骤 5: 计算 seal 值
import hashlib
def calculate_seal(audit_log_id, passphrase):
data = (audit_log_id + passphrase).encode('utf-8')
sha256_hash = hashlib.sha256(data).hexdigest()
return sha256_hash[:8].lower()
audit_log_id = "audit_1772291097099_7"
passphrase = "玄霄密令"
seal = calculate_seal(audit_log_id, passphrase)
# seal = "cbbf99d6"
步骤 6: 构建复核笔记
review_notes = f"""ctf_request={passphrase}
audit_log_id={audit_log_id}
seal={seal}"""
# 调用 review_document 提交复核
response = mcp_remote52pojie_review_document(
session_id="sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
doc_id="confidential:/audit/logs",
review_notes=review_notes
)
成功响应:
{
"status": "challenge_issued",
"tool_access_token": "tool_access:get_audit_log:1772291164477:96359ce3322ee3f2"
}
步骤 8: 读取审计日志,获取 _audit_token
audit_response = mcp_remote52pojie_get_audit_log(
session_id="sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
log_id="audit_1772291097099_7",
access_token="tool_access:get_audit_log:1772291164477:96359ce3322ee3f2"
)
审计日志响应:
{
"_audit_token": "audit_access:audit_1772291097099_7:1772291097099:95c5879d1a3e5b71"
}
步骤 9: 读取终卷,获取 Flag
final_doc = mcp_remote52pojie_get_document(
session_id="sess_c8eb85e2-203d-4601-871a-d7452fc94ce3",
doc_id="secret:/board/resolutions",
access_token="audit_access:audit_1772291097099_7:1772291097099:95c5879d1a3e5b71"
)
最终结果:
{
"id": "secret:/board/resolutions",
"title": "至高议会密议",
"content": "flag{new_year_2026_keep_warm}"
}
避坑总结
false_scroll 是干扰项,口令应使用 玄霄密令。
review_document 的复核字段必须放在 review_notes 文本里,按模板三行提交。
audit_log_id 必须用同一会话内最新拒绝返回值。
compliance_blob_b64 为干扰信息,可忽略。
tool_access_token 时效短,拿到后应立即调用 get_audit_log。