1. 从一个练手靶场说起XSS闯关到底在练什么第一次接触XSS闯关靶场的人多半会有两种反应一种是“这不就是弹个窗吗有什么难的”另一种是“怎么每一关都过不去明明payload看着没问题”。我当初属于后者前六关卡了整整一个下午后来才慢慢摸清楚这类靶场的设计逻辑——它不是在考你背了多少条payload而是在训练一种“输入点识别→上下文判断→绕过策略选择”的反射弧。XSS全称跨站脚本攻击核心就一句话让浏览器把攻击者构造的脚本当成页面自身逻辑来执行。听起来简单但真正落地的时候你会发现同样一句scriptalert(1)/script有的地方直接弹窗有的地方被转义成文本有的地方干脆被过滤得渣都不剩。这背后的差异就是所谓的“上下文”——你的输入最终落在HTML标签里、属性里、JavaScript代码块里还是URL参数里决定了你能用什么姿势突破。这套闯关靶场的价值就在这里。它把真实场景中常见的输出位置和过滤策略拆成一关一关让你在受控环境里反复试错。适合谁来练我的判断是刚学完XSS基础概念、能看懂HTML和JavaScript、但还没在真实项目里独立挖过漏洞的人。如果你已经能熟练绕过各种WAF那这个靶场对你来说偏基础但如果你连“闭合标签”和“闭合属性”的区别都还模糊那它是最好的起点。接下来的内容我会按关卡推进的顺序把每一关的核心考点、我实际用的payload、以及踩过的坑全部摊开讲。不是那种“第一关用这个第二关用那个”的流水账而是把每一关背后的判断逻辑讲透让你遇到新场景时能自己推导出解法。2. 闯关前的环境准备与基础认知2.1 靶场搭建与浏览器选择这类靶场通常提供在线环境但我的习惯是本地搭一份。原因很简单在线环境有时候会有网络延迟而且你没法随意改源码去观察过滤逻辑。本地搭建一般就是拉一个集成环境把靶场文件丢进Web根目录改一下数据库配置就能跑起来。具体步骤各版本略有差异但核心就三步部署Web服务、导入数据、访问入口页面。浏览器方面我强烈建议用Chrome或者Edge配合F12开发者工具。Firefox也行但Chrome的Elements面板在观察DOM变化时更直观。另外装一个可以快速修改和重放请求的插件会省很多时间不过这不是必须的手动改URL参数也能完成大部分关卡。提示本地搭建时记得把靶场的错误提示打开PHP的display_errors设为On。很多关卡你过不去不是因为payload不对而是因为某个字符触发了后端报错但错误被吞掉了你根本看不到。2.2 必须理解的三个核心概念在正式闯关之前有三个概念如果没搞清楚后面会非常痛苦。第一个是“输出上下文”。你的输入被服务器处理后最终会出现在HTML文档的某个位置。这个位置决定了浏览器如何解析你的输入。比如输入落在div这里/div中间那你需要构造能执行脚本的标签如果落在input value这里里面那你需要先闭合属性引号再构造新标签或者事件。第二个是“过滤与转义的区别”。过滤是直接删掉或替换危险字符比如把变成空转义是把特殊字符变成HTML实体比如把变成lt;。转义后浏览器不会把它当标签解析但如果你能找到一个地方服务器忘了转义或者转义不完整就有机会。第三个是“payload的闭合策略”。这是新手最容易忽略的。很多人拿到一个payload就直接往里塞结果发现页面结构被破坏了脚本根本没执行。正确的做法是先看你的输入被放在什么标签、什么属性、什么引号环境里然后决定是先闭合标签、先闭合属性、还是先闭合引号。2.3 常用payload速查与适用场景下面这张表是我自己整理的高频payload按上下文分类。注意这些payload不是万能的每一关都要根据实际情况调整。上下文位置推荐payload核心思路HTML标签之间scriptalert(1)/script直接插入脚本标签HTML标签之间script被过滤img srcx onerroralert(1)利用图片加载失败触发事件双引号属性内 onmouseoveralert(1)闭合属性并注入事件单引号属性内 onmouseoveralert(1)同上注意引号类型无引号属性内onmouseoveralert(1)直接追加事件注意前后空格JavaScript字符串内;alert(1);//闭合字符串并注释后续代码URL参数回显javascript:alert(1)利用伪协议执行这张表建议打印出来贴在显示器旁边前几关基本够用。但到了后面你会发现靶场会针对这些payload做各种变形过滤那时候就需要更灵活的构造方式。3. 逐关突破从反射型到存储型的完整实战3.1 第一关到第三关基础反射型的三种典型场景前三关通常是热身但热身不代表没有坑。第一关一般是没有任何过滤的反射型XSS。你输入什么它就原样输出在页面某个位置。这种关卡用最基础的scriptalert(1)/script就能过。但我要说的是不要因为简单就跳过观察。打开F12看你的输入落在哪个标签里是div还是p这决定了你后面遇到过滤时能不能快速判断该闭合什么。第二关开始有过滤了通常是针对script标签的。这时候img srcx onerroralert(1)就派上用场。但有些版本会把onerror也过滤掉那就换onload、onfocus、onclick等事件。我的习惯是准备一个事件列表一个一个试哪个没被过滤用哪个。常见可用事件包括onerror、onload、onmouseover、onfocus、onclick、oninput。第三关的典型设计是把输入放在属性里。比如input value你的输入。这时候直接写script是没用的因为它在属性值里面浏览器不会把它当标签解析。你需要先闭合双引号然后注入事件 onmouseoveralert(1)。注意这里最后那个引号的处理——如果服务器在输出时又给你补了一个引号你可能需要把后面的引号注释掉或者用其他方式闭合。注意在属性内注入事件时事件触发需要用户交互。比如onmouseover需要鼠标移上去onclick需要点击。如果靶场要求自动弹窗你可能需要换用onfocus配合autofocus或者用svg onloadalert(1)这种自动触发的事件。3.2 第四关到第六关过滤绕过与编码技巧从第四关开始靶场会加入各种过滤规则这时候就是真正考验构造能力的时候。第四关常见的过滤是替换或删除script关键字。比如你输入script它变成。这时候有两种思路一是用大小写混写比如ScRiPt有些简单的过滤只匹配小写二是用其他标签比如img、svg、body。我实测下来svg onloadalert(1)在很多过滤场景下都能过因为svg标签本身不是脚本标签但它的onload事件可以执行脚本。第五关可能会过滤alert关键字。这时候你可以用confirm、prompt代替或者用eval拼接字符串。比如eval(alert(1))。更高级一点可以用String.fromCharCode来构造字符串但那个写起来比较长适合过滤特别严格的时候。第六关我遇到的是对括号和引号的过滤。这时候你需要用反引号代替引号用throw代替括号或者用onerroralert1 这种模板字符串的写法。具体用哪种取决于靶场到底过滤了什么。我的建议是每关先用一个最简单的payload试探看它返回什么然后根据返回结果判断过滤规则再针对性构造。下面这张表是我在第四到第六关实际用过的payload按过滤类型分类过滤类型绕过payload说明过滤script标签img srcx onerroralert(1)利用img标签事件过滤script和imgsvg onloadalert(1)svg标签通常不被过滤过滤alertconfirm(1)或prompt(1)换弹窗函数过滤括号onerroralert\1模板字符串代替括号过滤引号onerroralert(String.fromCharCode(49))用字符编码构造过滤空格onerroralert(1)//用注释代替空格3.3 第七关到第九关存储型与DOM型的差异第七关开始进入存储型XSS。和反射型最大的区别是你的输入会被存进数据库然后每次访问页面都会执行。这意味着你不需要诱导受害者点击特制链接只要他访问了被污染的页面就会中招。从利用难度上说存储型比反射型更危险但从闯关角度说构造payload的逻辑是一样的只是提交后需要刷新页面或者重新进入列表页才能看到效果。第八关通常是DOM型XSS。这种关卡的特点是你的输入不经过服务器直接在浏览器端被JavaScript处理并插入到页面中。比如页面有个document.write(location.hash)之类的逻辑。这种关卡用Burp抓包是看不到payload的因为它在URL的#后面不会发送到服务器。你需要直接在浏览器地址栏构造或者用F12的Console面板手动执行。第九关我遇到的是综合过滤既有服务端过滤又有客户端过滤。这时候需要两边都绕过。服务端的过滤通常可以通过编码绕过比如URL编码、HTML实体编码、Unicode编码。客户端的过滤则需要看它的JavaScript逻辑找到它检查了什么、替换了什么然后构造一个它检查不到但浏览器能执行的payload。提示DOM型XSS的调试Chrome的Sources面板比Elements面板更好用。你可以在JavaScript代码里下断点一步一步看你的输入是怎么被处理的哪个函数做了过滤哪个函数做了插入。这比盲猜payload效率高得多。3.4 第十关及以后综合场景与思路总结第十关之后靶场通常会模拟更真实的场景比如留言板、搜索框、用户资料页等。这些场景的特点是输入点更多过滤规则更复杂但核心思路不变找到输入点判断输出上下文选择闭合策略绕过过滤规则。我在最后一关卡了很久原因是它同时过滤了、、、和script。最后用的payload是img srcx onerroralert(1)的变体把alert换成confirm把括号用反引号代替最终构造出img srcx onerrorconfirm\1。这个payload能过的原因是靶场过滤了alert但没过滤confirm过滤了单双引号但没过滤反引号。这一关给我的最大启发是不要假设某个payload一定不能用也不要假设某个字符一定被过滤了。每次都要实际测试根据返回结果调整。很多时候你以为被过滤的字符其实只是被转义了换个位置或者换个编码方式就能用。4. 常见问题与排查技巧实录4.1 payload不弹窗的六种可能原因这是新手问得最多的问题“我的payload明明没问题为什么不弹窗”根据我的经验原因通常逃不出下面六种。第一种上下文判断错误。你以为输入在标签之间实际上它在属性里面。这时候script不会被解析你需要先闭合属性。排查方法F12看Elements面板找到你的输入看它被什么标签和属性包裹。第二种特殊字符被转义。你输入的被转成了lt;浏览器把它当文本显示。排查方法看页面源代码搜索你的输入看它变成了什么。如果看到lt;、gt;、quot;这些说明被转义了。第三种关键字被过滤。你输入的script被删掉了或者alert被替换成空。排查方法提交一个包含敏感关键字的测试字符串看返回结果里哪些字符消失了。第四种事件没有触发。你用了onmouseover但没有鼠标移上去你用了onclick但没有点击。排查方法换用自动触发的事件比如onload、onerror、onfocus配合autofocus。第五种payload被截断。服务器对输入长度有限制你的payload太长后半截被切掉了。排查方法提交一个长字符串看返回结果的长度。第六种浏览器安全策略拦截。某些浏览器对特定标签或事件有额外限制比如svg在某些版本下不执行onload。排查方法换浏览器测试或者换标签。4.2 过滤规则探测的标准流程当你不知道靶场过滤了什么的时候不要瞎猜按下面这个流程走一遍基本能摸清规则。第一步提交一个包含所有特殊字符的测试字符串比如 ( ) ; : / \ #。看返回结果里哪些字符还在哪些消失了哪些被转义了。第二步提交一个包含常见关键字的测试字符串比如script alert onerror img svg。看哪些关键字被删了哪些被替换了。第三步根据前两步的结果构造一个最小化的payload只包含没被过滤的字符和关键字。第四步如果最小化payload还是不执行检查上下文调整闭合策略。这个流程我用了很多次基本上十分钟内就能摸清一个关卡的过滤规则。比盲目试payload效率高得多。4.3 编码绕过的三个实用技巧当过滤规则比较严格的时候编码绕过是最后的杀手锏。下面三个技巧是我用得最多的。HTML实体编码。比如alert可以写成#97;#108;#101;#114;#116;。这种编码在HTML解析时会被还原成原始字符但过滤规则如果只匹配明文就绕过去了。注意这种编码只在HTML上下文里有效在JavaScript代码块里无效。URL编码。比如alert可以写成%61%6c%65%72%74。这种编码在URL参数里有效服务器解码后得到原始字符。但如果服务器解码后又做了过滤那就没用。JavaScript编码。比如alert可以写成\u0061\u006c\u0065\u0072\u0074。这种编码在JavaScript字符串里有效浏览器执行时会还原。适合输入落在JavaScript代码块里的场景。注意编码绕过不是万能的。如果服务器在解码后又做了过滤或者浏览器对编码后的内容有额外限制都可能失败。我的经验是先试最简单的编码不行再换复杂的不要一上来就用最复杂的浪费时间。4.4 常见问题速查表问题现象可能原因解决方法输入原样显示但不执行被转义或上下文错误查看源代码确认是否被转义检查标签属性输入部分消失关键字被过滤换用其他标签或事件尝试编码绕过弹窗函数不执行alert被过滤换confirm或prompt用eval拼接事件不触发需要用户交互换自动触发事件加autofocuspayload被截断长度限制缩短payload用短标签和短事件刷新后不执行存储型未入库检查提交是否成功查看数据库或列表页这张表建议保存下来遇到问题先查表查不到再按4.2的流程走一遍。5. 从靶场到实战经验沉淀与进阶方向5.1 靶场和真实环境的差距打完这个靶场你可能会觉得自己已经会XSS了。但我要泼一盆冷水靶场和真实环境差距很大。靶场的过滤规则是固定的、简单的真实环境的过滤规则是动态的、复杂的。靶场的输出位置是明确的真实环境的输出位置需要你自己去发现。靶场的payload可以直接用真实环境的payload需要根据业务逻辑定制。我在实际项目里遇到的第一个XSS输入点在一个JSON接口的返回值里前端用JavaScript动态插入到页面。这种场景在靶场里没有但核心思路是一样的找到输入点判断输出上下文构造payload。区别在于你需要先抓包分析接口再跟踪前端代码最后才能确定payload。所以靶场练的是基本功真实环境练的是综合能力。基本功不扎实真实环境寸步难行但只练基本功到了真实环境还是会懵。5.2 我踩过的三个典型坑第一个坑忽略大小写。有一次靶场过滤了script我试了script、SCRIPT、Script都不行最后发现它用的是不区分大小写的正则。这时候换标签才是正解死磕大小写是浪费时间。第二个坑忘记闭合。在属性内注入时我只写了onmouseoveralert(1)但忘了前面的引号闭合。结果payload被当成属性值的一部分根本没触发事件。后来加上才成功。这个坑我踩了不止一次每次都是因为太着急。第三个坑忽略浏览器差异。同一个payload在Chrome里能弹窗在Firefox里不弹。原因是Firefox对某些标签的事件支持不一样。后来我养成了习惯每关至少用两个浏览器测一遍确保payload的兼容性。5.3 后续可以怎么继续练如果你已经打完了这个靶场想继续提升我有三个建议。第一找其他类型的靶场练手。XSS只是Web安全的一个方向还有SQL注入、文件上传、命令执行等。每个方向的思路不一样多练几个能拓宽视野。第二自己搭一个简单的Web应用故意留几个XSS漏洞然后自己挖。这个过程能让你理解开发者的思维知道漏洞通常出现在哪里。第三看一些公开的漏洞报告分析别人是怎么发现和利用XSS的。注意看他们的思路而不是只看payload。思路比payload重要得多。最后分享一个小技巧每次打完一关不要急着下一关先把这一关的payload和思路记下来。过一周再回来看如果你能不看笔记重新构造出payload说明你真的掌握了。如果不行那就再练一遍。这个方法看起来很笨但效果很好。