SEECON 13 复现学习一下SEECON 13 的题目
self-ssrf 拿到题目里的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import express from "express" ;const PORT = 3000 ;const LOCALHOST = new URL (`http://localhost:${PORT} ` );const FLAG = Bun .env .FLAG !!;const app = express (); app.use ("/" , (req, res, next ) => { if (req.query .flag === undefined ) { const path = "/flag?flag=guess_the_flag" ; res.send (`Go to <a href="${path} ">${path} </a>` ); } else next (); }); app.get ("/flag" , (req, res ) => { res.send ( req.query .flag === FLAG ? `Congratz! The flag is '${FLAG} '.` : `<marquee>🚩🚩🚩</marquee>` ); }); app.get ("/ssrf" , async (req, res) => { try { const url = new URL (req.url , LOCALHOST ); if (url.hostname !== LOCALHOST .hostname ) { res.send ("Try harder 1" ); return ; } if (url.protocol !== LOCALHOST .protocol ) { res.send ("Try harder 2" ); return ; } url.pathname = "/flag" ; url.searchParams .append ("flag" , FLAG ); res.send (await fetch (url).then ((r ) => r.text ())); } catch { res.status (500 ).send (":(" ); } }); app.listen (PORT );
看不懂完全没思路,去看了某大佬的wp,直接分析到express框架的源码了,等等的逻辑,直接被吓死。。。。 自己打也许不能分析出来,但还是学习一下思路:
通过/ssrf访问flag时会在末尾加上参数flag,并给予参数正确flag值, 我的问题是,那直接不带任何参数去访问/ssrf不就能只有ssrf函数加上的正确的flag参数和值了吗?
然而还是怪我不了解node.js 的 express 框架。
app.use(“/“, …) 的作用范围: 这个中间件匹配所有路径(因为 / 是所有路径的前缀),所以它会在任何路由(如 /ssrf, /flag)之前执行。这是一个常见的“全局前置检查”。 所以简单来说,想要访问/ssrf就得带上flag参数,这样的话就无法实现空参数访问/ssrf了。
而wp 中, 解决办法就是通过express对url的处理来解题 express在处理相同的参数时会用逗号拼接:
1 /?flag =1&flag=flag -> req.query.flag = '1,flag'
这样会导致flag值与正确值不匹配
但是若是传入:ssrf/?flag[=]=asd
会导致两次解析不一致,其中express的qs解析一次,url解析一次:
url把flag[=]= 拆分为: 参数:flag[= 值: ]=asd
qs把 flag[=]= 解析为对象 { flag: { “=”: “asd” } } 此时 req.query.flag 是一个对象 { ‘=’: ‘asd’ },不是 undefined
第一次解析完成后,会导致qs认为flag是存在的,从而通过了’/‘中间件,进入到’/ssrf’, 之后经过’/ssrf’的处理,会变成:
ssrf/?flag[=]=asd&flag=(正确flag值)
然后解析:
url: 参数1:flag[ 值:]=asd 参数2:flag 值: (正确flag)
qs就不会把url里两个不同的参数进行合并,所以就存在flag=正确值
感觉若是有node.js的开发经验,那这种题目应该算简单?可惜我学信安并没有很多开发经验。要是考到不了解的框架那不知道怎办了。 来自qwen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 你的感受**非常真实,也非常普遍** ,几乎所有从安全入门、没有深厚开发背景的人都会经历这个阶段。但请放心,你的思考方向完全正确,而且你已经迈出了最关键的一步——**理解了漏洞的本质,而不仅仅是“套用技巧”** 。 我们来深入聊聊你的担忧,并给你一些实用的建议。 ---### 1. “有开发经验就简单?” —— 部分正确,但不绝对 ✅ **有开发经验的优势** :- 熟悉框架的“正常行为”,更容易识别“异常行为”。- 知道 `app.use()` 是中间件,`req.query` 是什么,`URLSearchParams` 怎么工作。- 对 URL 编码、参数解析、HTTP 流程有直觉。 ❌ **但不代表“简单”** :- 这道题利用的是 `qs` 库在**嵌套参数 + 重复键名** 下的**非预期行为** ,即使是经验丰富的 Node.js 开发者,如果不专门研究过 `qs` 的源码或边界情况,也未必能立刻想到。- CTF 题目的设计目标就是**挖掘框架/库的边缘 case 和逻辑矛盾** ,而不是考察“日常开发能力”。 所以,这道题对开发者来说可能“更容易理解”,但要说“简单”,那也不一定。它更像是一个“**知道就知道,不知道就很难猜** ”的知识盲点题。 ---### 2. “没开发经验怎么办?” —— 别怕,有方法! 你现在的状态是:**通过学习 WP,你补上了知识盲点,并且用自己的逻辑重新推导了一遍** 。这比“凭空想出”更有价值!因为:> **CTF 不是考试,而是研究。真正的安全研究者都是站在巨人的肩膀上。** #### 🛠️ 如何弥补开发经验的不足? | 方法 | 具体做法 | 效果 | | :--- | :--- | :--- | | **1. 学习基础 Web 开发概念** | 不需要精通一门语言,但要懂:<br > - HTTP 请求/响应结构(GET/POST, Headers, Query, Body)<br > - Cookie/Session<br > - 常见框架模式(路由、中间件、模板渲染)<br > - 数据库基础(SQL/NoSQL) | 建立“系统观”,不再把代码当黑盒 | | **2. 主动查文档和源码** | 看到 `express` , `qs` , `flask` , `django` 等关键字:<br > - 立刻去搜官方文档或 GitHub。<br > - 重点看:**参数解析、路由匹配、安全机制** 。<br > - 例如:`qs` 的 README 明确写了它支持嵌套对象和数组。 | 把“未知”变成“已知” | | **3. 搭建实验环境** | 本地写几行代码验证你的猜想:<br > ```js<br>const qs = require('qs');<br>console.log(qs.parse('flag[=]=asd&flag=123'));<br>// 输出?<br>``` | 实践出真知,记忆更深刻 | | **4. 关注“框架特性”而非“语言”** | Node.js/Python/PHP 只是载体,真正重要的是:<br > - Express 的中间件机制<br > - Flask 的 template injection<br > - Django 的 ORM 注入<br > 掌握一种,触类旁通。 | 提升学习效率 | ---### 3. “考到不了解的框架怎办?” —— 核心策略 当遇到完全陌生的框架时,别慌,按以下步骤走:#### 🔍 步骤 1:快速识别技术栈 - 看文件名:`app.py` → Python/Flask? `server.js` → Node.js?- 看依赖:`package.json` → Node.js, `requirements.txt` → Python- 看错误信息:500 错误可能暴露框架名#### 🧩 步骤 2:找“关键函数”和“数据流” - 找输入点:`req.query` , `req.body` , `request.GET` , `request.POST` - 找输出点:`res.send()` , `return render(...)` - 找危险操作:`eval` , `exec` , `os.system` , `数据库查询` , `文件读写` , **SSRF 中的 `fetch`/`requests.get`** #### 📚 步骤 3:查该函数的文档 - 搜索:`express url.searchParams.append` 或 `flask request.args.to_dict` - 重点看:**如何处理重复参数?如何处理特殊字符?是否有已知 CVE?** #### 💡 步骤 4:联想类似漏洞 - SSRF?→ 想办法绕过 host/protocol 检查- SQLi?→ 想办法闭合引号或注释- SSTI?→ 尝试 `{%}` , `{{}}` , `<%= %>` - 参数污染?→ 试试 `?a=1&a=2` ---### 🌟 最后一句话 > **“开发经验”不是门槛,而是你可以逐步积累的工具箱。** > > **而你现在已经拥有了最宝贵的资产——分析问题的能力。** 继续加油!你完全有能力攻克任何题目。💪