首页 / 资讯中心 / 文章详情

XSS-Labs通关攻略:从上下文思维到浏览器解析链的XSS实战指南

XSS-Labs通关攻略:从上下文思维到浏览器解析链的XSS实战指南 ★ FEATURED ARTICLE
先坦白一件事我收藏夹里存过的XSS payload最多的时候超过一百条。但真正把 XSS-Labs 从头刷到后几关之后我才意识到一个事实——XSS 这个漏洞难的不是记住多少个alert(1)姿势而是搞清楚你注入的数据到底落进了 HTML 的哪一段、那一层由谁解析、过滤规则挡的是哪一步。XSS-Labs 的价值恰好就在这里它把 XSS 最常见的注入场景压成十多个关卡逼着你用“上下文思维”去解题而不是靠运气拼 payload。这篇攻略我不想逐关贴答案因为不同社区流传的版本关卡顺序和过滤规则多少有点出入纯背答案几分钟就忘了。我更想把从第一关到后段挑战关会反复用到的判断思路、闭合技巧、绕过方式拆开讲明白配合可以直接抄的 payload 样例。适合刚开始系统刷 XSS、刷到某关卡住、或者刷完想回头梳理原理的人。所有操作都在本机 PHP 靶场环境里进行不牵扯任何线上目标。1. 环境准备部署靶场与建好调试工具链1.1 把靶场源码跑起来XSS-Labs 最常见的形态是一套 PHP 源码下载后扔进本地 PHP 集成环境的 Web 根目录就能跑。我本机的目录看起来是这样D:/php-env/WWW/xsslabs/ ├── index.php ├── level1.php ├── level2.php └── ...启动 Apache 后直接访问http://127.0.0.1/xsslabs/能看到关卡列表页就算部署成功。个别版本需要数据库通常是建一个库导入 SQL 就行大多数版本根本不需要。这里有个容易被忽略的小坑PHP 版本太新的话页面上可能冒出Deprecated警告。这些警告本身不影响注入测试但它会混进 HTML干扰你分析输出点结构。建议在 PHP 配置里关掉display_errors或者把错误级别调低让页面输出保持干净。1.2 先养成“看渲染结果”的习惯刷这套靶场我的固定动作是先 F12 看 Elements再回头看 PHP 源码。比如level1.php里可能是echo $name;也可能是echo input value$name;这两种代码对应完全不同的注入策略前者是文本节点后者是属性内部。很多看似复杂的关卡只要先把输出点看清payload 已经成型一半。XSS-Labs 每一关几乎都在训练同一件事一眼识别输出点上下文。1.3 工具链清单浏览器 F12 开发者工具看 DOM 结构、网络请求这是最核心的工具。抓包工具后面改 Referer、User-Agent、Cookie 注入时没有它寸步难行。一个本地文本文件记录所有有效 payload 和失败原因别看这个习惯土后期特别有用。一个简单的脚本Python 或 Node.js 都行用来批量发送 payload 验证过滤规则。工具不用多但缺一个都可能卡关。2. 前四关先搞懂 XSS 的三个基础接入点2.1 第一关的直给文本节点直接注入第一关通常是没有任何过滤的参数直接拼到某个标签中间。访问http://127.0.0.1/xsslabs/level1.php?namescriptalert(1)/script弹窗成功。这里真正要理解的是浏览器解析 HTML 时遇到script标签会把它当作脚本块执行。当你的输入落在标签的文本区域里并且没有过滤你就获得了“插入最完整结构”的能力。同一关如果你把 payload 换成img srcx onerroralert(1)一样能弹窗。这说明 XSS 的第一原则先确定上下文再选择执行载体。2.2 第二关的变化从文本节点跳到 HTML 属性里第二关开始输入被放进了这样的结构input value这里是你输入的内容如果继续用第一关的思路传scriptalert(1)/script浏览器会把它当成 value 属性的一部分结果是input valuescriptalert(1)/script脚本不会执行。这时要做的是“闭合”。先用一个双引号把 value 闭合掉再用把 input 标签收尾页面结构就变成input valuescriptalert(1)/script所以 payload 是scriptalert(1)/script这类闭合的核心就是“逃逸上下文”。你可以先闭双引号或单引号也可以先闭顺序取决于输入点前是什么符号。2.3 第三关单引号属性与事件注入这一关如果是单引号包裹input value$name同时后端可能对双引号、尖括号做了转义但老版本 PHP 的htmlspecialchars默认不转义单引号于是单引号闭合路线仍然可行 autofocus onfocusalert(1)渲染后的 HTMLinput value autofocus onfocusalert(1)页面加载时 autofocus 自动聚焦到输入框onfocus 事件触发弹窗。这关的价值在于让你意识到XSS 的载体绝不只是script标签HTML 事件属性同样是常规入口而且很多时候比script更隐蔽因为它不容易被“删标签”型规则拦下来。2.4 第四关过滤script时的双写思路第四关服务端很可能写出了类似这样的过滤$name str_replace(script, , $name);直接提交script会被整体删掉。双写就可以绕过scrscriptiptalert(1)/script服务端删掉中间那个完整的script后剩下的scr和ipt恰好拼回script。这个技巧的本质是过滤逻辑往往只做一次非递归的字符串替换删除动作本身反而帮你重组出了目标关键字。从这关开始就正式进入“读过滤规则”阶段了。每次失败先别急着换弹窗姿势去看看过滤函数是过滤script标签还是过滤script字符串是str_replace还是preg_replace大小写敏感吗这些细节直接决定用双写、大小写还是编码绕过。3. 第五关到第八关黑名单规则下的几种典型绕过3.1 大小写混合HTML 解析器不区分标签名大小写如果过滤规则写了str_replace(script, , $name)并且只匹配小写变体那么大写直接绕过SCRIPTalert(1)/SCRIPT浏览器对 HTML 标签名的大小写不敏感SCRIPT和script在解析层没有任何区别但字符串匹配层的过滤往往就分大小写。这是黑名单型过滤器最常见的破绽之一。顺便说一句别只会对标签名做大小写事件属性名同样可以img srcx Onerroralert(1)属性名在 HTML 解析中也不分大小写。3.2 关键字被删时的双写与拼接再往后可能碰到“关键字被删”型过滤。常见的对策已经整理成表格过滤目标绕过写法原理scriptscrscriptipt删除内部后两侧拼接scriptscrscriptipt同样可用关键字重组onerrorimg srcx onerroronerroralert(1)删除一次后剩余完整关键字javascriptjava#x73;cript:属性值中实体解码需要特别提醒这些写法只对“非循环替换”的过滤规则有效。如果后端用了preg_replace加循环匹配或者过滤逻辑执行了两次双写就会失败。正确姿势是先提交一个测试字符串看服务端怎么处理再决定下一步。3.3 伪协议被封死标签之后的另一条路有些关卡把script、on关键字封得很死但a标签是放开的那就用href属性试试a hrefjavascript:alert(1)点我/a浏览器在点击链接时执行javascript:伪协议。这个方案通常需要用户交互测试起来不如事件属性方便但它是绕过“禁用标签”类过滤的重要手段。3.4 实体编码给关键字换一层伪装如果javascript:也在过滤名单里可以在属性值里用 HTML 实体编码a hrefjava#x73;cript:alert(1)点我/a浏览器解析 HTML 属性值时会先把#x73;解码成s于是href拿到的仍是javascript:alert(1)。过滤规则匹配的是源码字符串浏览器执行的是解码后的字符这个“解码时间差”就是大量编码绕过的基础。4. 第九关到第十三关注入点不只是 URL 参数4.1 隐藏参数从源码里翻出来的输出点到第 9、10 关左右输出点可能藏在一个容易被忽略的参数里。典型场景是页面里有一堆表单项比如t_link、t_history、t_sort但真正被 echo 出来的只有其中一个。处理思路很直接先看前端源码和 PHP 源码梳理哪些参数被$_GET、$_POST或$_REQUEST接收哪些参数最终参与输出。这类关卡就是练“找参数”的耐心看起来难其实只是入口藏得深。找到隐藏参数后依然是先看输出点上下文再决定闭合方式。如果被过滤可以尝试闭合引号后注入事件属性构造出完整属性而不用引入新标签。4.2 HTTP 头注入改 Referer、User-Agent 和 Cookie再往后服务端开始把请求头拼进页面。常见输出是打印Referer或User-Agent这时候就该用抓包工具了。在抓包工具里拦截请求把 Referer 改成scriptalert(1)/script或者把 User-Agent、Cookie 改成类似内容服务端没过滤的话刷新页面就会触发弹窗。这类关卡最容易让人卡住因为直觉里 XSS 只在 URL 参数里但 HTTP 头也是数据入口服务端只要把它当字符串拼到页面就是反射点。“数据入口”和“输出点”是两码事这句话放到真实漏洞挖掘里同样成立。4.3 多参数组合与编码叠加有些关卡单点注入会失败但多个参数拼接后就能闭合。比如一个参数负责输出另一个参数输出script服务端如果恰好把它们拼在相邻位置最终就会组装成完整 payload。应对办法是提交多组参数后F12 看渲染后的整体 HTML不要死盯单个参数点的 payload。这种“多参数组合”题在真实场景里非常常见很多 WAF 绕过也是利用参数拼接的思路。5. 通关后的复盘从背 payload 转向理解浏览器解析链5.1 XSS-Labs 教会我的解析顺序模型刷完一轮后建议把每一关的“上下文 过滤方式 绕过原理”整理成表。你会发现几乎所有 trick 都指向同一套浏览器解析顺序HTML 实体解码#x73;变回sHTML 结构解析确定标签、属性、文本节点的边界属性值内的 URL 解析识别http、javascript等协议JavaScript 引擎执行让 payload 真正生效过滤规则通常作用在第 1 步之前的字符串上浏览器却从第 1 步开始逐层解析。只要某一层没被滤干净后续就有继续推进的空间。5.2 用脚本批量验证但别迷信“响应里出现”我用 Python 写过一个简单的批量测试脚本把候选 payload 列表发到关卡地址检测响应里是否包含alert(1)等特征字符串。这类脚本能快速筛掉被过滤的选项但有一个坑必须注意响应里出现关键字不代表页面真的执行了。比如 payload 被错误转义成lt;scriptgt;alert(1)lt;/scriptgt;响应源码里仍然有alert(1)但浏览器不会执行。所以自动化验证之后至少保留一个手动浏览器环节或者用无头浏览器监听 alert 回调。脚本提升效率但不能替代真实渲染结果。5.3 回到防御视角通关之后顺手做三件事如果既做测试也写代码刷完靶场后可以立刻把原理转化成防御动作所有用户可控数据在拼进 HTML 前统一走htmlspecialchars($output, ENT_QUOTES, UTF-8)输出编码给敏感 Cookie 加 HttpOnly 属性就算脚本能执行也拿不到会话凭证配置Content-Security-Policy响应头限制脚本来源给 XSS 做最后一层兜底。这三件事分别对应你在靶场里用过的多数利用路径也是实际项目里防 XSS 的底线。再往深了说还有字符集攻击、mXSS 这些更偏研究的方向可以以后单独聊。最后分享一个我自己的小习惯刷这种关卡型靶场我会单独维护一份“失败记录”把每次失败的 payload 和失败原因写下来。刚开始觉得多余到后段关卡才发现很多卡住不是因为不会某个 payload而是忘了前面已经排除了哪条路径。把失败路径梳理清楚剩下的选择往往就那么一条。
阅读完成 · 觉得有帮助?
咨询建站