Level 1:裸奔的输出
- 因果逻辑:数据落在 <h2>[这里]</h2> 之间。由于后端没做任何过滤,浏览器会直接把输入当 HTML 指令。
- Payload:?name=<script>alert(1)</script>
- 通关动作:直接回车,页面解析脚本并弹窗。
Level 2:属性的逃逸
- 因果逻辑:数据在 <input value="[这里]">。直接写脚本会被当成属性值。
- 绕过思路:利用 " 逃离属性,利用 > 逃离标签。
- Payload:?keyword="><script>alert(1)</script>
- 通关动作:闭合原标签后,新标签 <script> 开始生效。
Level 3:不严谨的转义(单引号注入)
- 因果逻辑:数据在 <input value='[这里]'>。后端用了 htmlspecialchars(),但该函数默认不转义单引号 ',且无法拦截标签内的事件属性。
- 绕过思路:既然不能用尖括号写新标签,就利用 onmouseover 事件。
- Payload:?keyword=' onmouseover='alert(1)
- 通关动作:鼠标移动到输入框上。
Level 4:正则过滤的死角(双引号注入)
- 因果逻辑:数据在 <input value="[这里]">。后端正则删除了 < 和 >,但漏掉了双引号 "。
- 绕过思路:用双引号闭合属性,注入不需要尖括号的事件。
- Payload:?keyword=" onmouseover="alert(1)
- 通关动作:鼠标移动到输入框上。
Level 5:黑名单防御(伪协议注入)
- 因果逻辑:后端会把 on 替换成 o_n,把 script 替换成 scr_ipt。
- 绕过思路:所有“事件”和“脚本标签”都废了。改用 <a> 标签的 href 属性,利用 javascript: 伪协议绕过检测。
- Payload:?keyword="> <a href="javascript:alert(1)">click</a>
- 通关动作:必须手动点击生成的那个 "click" 链接。
Level 6:大小写的疏忽
- 因果逻辑:黑名单很全(过滤了 href, on, script),但后端的替换函数 str_replace 是区分大小写的。
- 绕过思路:HTML 对属性名不区分大小写,改变大小写即可躲过过滤。
- Payload:?keyword="> <a HREF="javascript:alert(1)">click</a>
*(或者:?keyword=" OnMouseOver="alert(1)) - 通关动作:点击链接或滑过输入框。
Level 7:不递归的替换
- 因果逻辑:后端会将敏感词删掉,但只执行一次(非递归)。
- 绕过思路:构造“套娃”字符串 scscriptript。
- Payload:?keyword="> <scscriptript>alert(1)</scscriptript>
- 通关动作:后端删掉中间的 script 后,两边剩下的字母自动合体成新的 script。
Level 8:利用浏览器的实体解析
- 因果逻辑:输出在 href 内部,关键字全被拉黑。
- 绕过思路:利用 href 属性在执行前会进行 HTML 实体解码 的特性。
- Payload:?keyword=javascript:alert(1)
- 通关动作:点击页面下方的“友情链接”。
Level 9:特定的逻辑校验
- 因果逻辑:后端强制要求输入包含 http://。
- 绕过思路:在 Payload 中利用 JS 注释 // 塞进网址,骗过后端校验。
- Payload:?keyword=javascript:alert(1)//http://
- 通关动作:点击页面下方的“友情链接”。
Level 10:隐藏字段的曝光
- 因果逻辑:数据掉进了 type="hidden" 的隐藏表单。由于元素不可见,任何鼠标事件(onclick/onmouseover)都无法触发。
- 绕过思路:注入 type="text",强行修改标签属性让它显示出来。
- Payload:?t_sort=" type="text" onmouseover="alert(1)
- 注意:参数名必须是 t_sort。
- 通关动作:移动鼠标到新出现的输入框上。
Level 11:Referer 溯源注入
- 因果逻辑:后端代码里有一句 $_SERVER['HTTP_REFERER']。它会读取你“从哪个页面跳过来”的链接,并把它输出在隐藏表单中。
- 绕过思路:直接在 URL 里传参没用,必须用 Burp Suite 拦截请求,修改 Referer 字段。
- Payload:在 Burp 中修改:Referer: " type="text" onmouseover="alert(1)"
- 通关动作:提交修改后的数据包,鼠标滑过页面上新出现的输入框。
Level 12:User-Agent 浏览器指纹注入
- 因果逻辑:后端读取 $_SERVER['HTTP_USER_AGENT'] 并输出。开发者以为浏览器指纹是系统生成的,不会有毒。
- 绕过思路:同 L11,拦截数据包,修改 User-Agent 字段。
- Payload:User-Agent: " type="text" onmouseover="alert(1)"
- 通关动作:提交后,鼠标滑过新出现的输入框。
Level 13:Cookie 注入
- 因果逻辑:后端读取 $_COOKIE['user'] 字段并输出。
- 绕过思路:拦截包,找到 Cookie 字段中的 user=... 部分进行修改。
- Payload:Cookie: user=" type="text" onmouseover="alert(1)"
- 通关动作:提交后,鼠标滑过新出现的输入框。
Level 14:Exif 盲区(此关通常因外链失效无法复现)
- 因果逻辑:本来是利用图片文件里的 Exif 信息(拍摄设备、经纬度等)进行 XSS。
- 面试谈资:这关在实战中很有意义——文件上传 XSS。如果一个网站允许上传图片,你可以在图片的元数据里塞进脚本,服务器在读取图片信息展示时就会中招。
Level 15:AngularJS 模板注入
- 因果逻辑:后端使用了 AngularJS 框架,并使用了 ng-include 指令。
- 绕过思路:ng-include 会包含并执行另一个页面的内容。我们可以让它包含第一关(L1),因为 L1 没有任何过滤。
- Payload:?src='level1.php?name=<img src=1 onerror=alert(1)>'
- 通关动作:自动弹窗。这是典型的前端框架 XSS,面试时提到 ng-include 会显得你懂前端架构。
Level 16:被过滤的空格
- 因果逻辑:后端把 script、on 以及空格都给过滤(或替换)了。
- 绕过思路:在 HTML 和 JS 中,除了空格,回车符 (%0a) 也可以起到分隔符的作用。
- Payload:?keyword=<img%0asrc=1%0aonerror=alert(1)>
- 通关动作:自动弹窗。利用回车符绕过了对空格的正则检测。
首先得用一个支持flash插件的浏览器
Level 17:嵌入式插件的属性注入 (Embed)
因果逻辑:页面使用了 <embed> 标签来插入多媒体插件。数据通过 arg01 和 arg02 传入并直接拼接到 src 属性后面。
绕过思路:由于数据是拼接到 src 属性内的,我们无法闭合 src(后端过滤了引号和尖括号),但我们可以注入其他的 事件处理器。
Payload:?arg01=a&arg02= onmouseover=alert(1)
通关动作:鼠标划过那个黑色的插件区域。
- 原理:虽然没有引号,但 HTML 允许不带引号的属性。浏览器解析为 <embed src="...a" onmouseover=alert(1)>。
Level 18:同样的 Embed,不同的触发
因果逻辑:与 L17 完全一致,只是场景稍有变化。
绕过思路:依然利用属性间不需要引号的特性。
Payload:?arg01=a&arg02= onmouseover=alert(1)
通关动作:划过插件区域。
- 面试价值:这关证明了在一些老旧组件或特定标签中,即使开发者转义了引号和尖括号,只要空格没过滤,攻击者依然可以增加新的属性。
Level 19:Flash XSS 的绝响 (已基本失效)
- 因果逻辑:这是经典的 Flash XSS。利用 Flash 文件(.swf)内部的 getURL 或 ExternalInterface.call 函数执行 JS。
Level 20:Flash + 编码绕过
- 因果逻辑:在 L19 的基础上增加了对非法字符的过滤。
- 绕过思路:通过对 Payload 进行十六进制编码或利用 Flash 内部的解析特性绕过。
- 结论:这关更多是针对特定版本 Flash 插件的漏洞,了解其“参数可控导致脚本执行”的本质即可。