1. 从零理解XSS为什么这19关值得反复刷很多人第一次接触XSS跨站脚本攻击时觉得概念特别简单——不就是往页面里塞一段JavaScript吗我当初也这么想结果真正上手打靶场的时候第一关就卡住了。后来才明白XSS的难点从来不是注入JS这个动作本身而是绕过各种过滤、编码、上下文限制让注入的代码真正在目标浏览器里执行。这套XSS Challenges从第1关到第19关几乎把Web开发中常见的XSS防御手段和对应的绕过思路都覆盖了一遍是打基础阶段性价比极高的练习材料。这篇文章我会按照自己的通关顺序把每一关的注入点判断、上下文分析、payload构造、绕过思路完整拆开讲。不是简单贴答案而是把为什么这个payload能过、为什么那个不行讲清楚。适合已经了解HTML和JavaScript基础、想系统掌握XSS实战技巧的朋友。如果你连script标签和事件属性都还没分清建议先补一下前端基础再来看不然会有点吃力。先说一个贯穿全程的核心方法论打XSS靶场第一步永远是判断输出上下文。你注入的内容最终出现在HTML的什么位置是标签之间、属性值里、还是JavaScript代码块中不同上下文对应的payload完全不同。很多人上来就甩scriptalert(1)/script结果发现被转义了或者根本不执行就是因为没做上下文分析。这19关里至少有三分之一是在训练这个判断能力。另外提前说明本文所有内容仅用于安全学习和防御研究请勿用于任何未授权的测试。靶场环境是专门搭建来练习的真实业务系统上乱来是要负法律责任的。2. 前五关从最裸的注入到属性上下文突破2.1 第1-3关无过滤直出与基础标签注入第1关基本是送分题。页面直接把URL参数拼接到HTML里输出没有任何过滤。你只要在参数后面接scriptalert(document.domain)/script就能弹窗。这一关的意义在于让你确认输出点位置——打开浏览器开发者工具看你的输入落在了哪个标签的哪个位置这是后面所有关卡的基础动作。第2关开始有了变化。它把输入放进了input标签的value属性里类似这样input value你的输入这时候你直接写script是没用的因为它在属性值内部浏览器会把它当成属性值的一部分。正确做法是先闭合属性再注入标签。payload是scriptalert(document.domain)/script这里的闭合了value属性的引号闭合了input标签然后插入script标签。这一关教的是属性上下文下的闭合思路。第3关的过滤稍微升级它把和做了转义或者过滤。这时候script标签用不了了但可以换事件属性。比如 onmouseoveralert(document.domain)或者用onfocus配合autofocus autofocus onfocusalert(document.domain)autofocus让输入框自动获得焦点onfocus事件随即触发不需要用户交互就能弹窗。这个技巧在后面很多关都会用到非常实用。2.2 第4-5关大小写与编码绕过初体验第4关开始对script关键字做过滤但它只过滤了小写的script。这时候最简单的绕过就是大小写混写ScRiPtalert(document.domain)/ScRiPtHTML标签名是大小写不敏感的浏览器照样解析执行。这一关告诉你一个道理过滤规则写得不够严谨等于没写。很多初级防御就是简单替换小写关键字绕过成本极低。第5关进一步它可能把script这个字符串整个过滤掉了不管大小写。这时候可以用嵌套构造来绕过。比如输入scrscriptipt过滤函数删掉中间的script后剩下的部分重新拼接成script。这是一种经典的过滤绕过思路——利用过滤函数删除一次的特性构造出删除后刚好成型的payload。这一关还有一种思路是用其他标签比如img srcx onerroralert(document.domain)。onerror在图片加载失败时触发srcx保证加载失败弹窗就执行了。这个payload在后面会反复出现是XSS里最常用的向量之一。3. 中段关卡编码、闭合与过滤逻辑的博弈3.1 第6-8关HTML实体编码与属性闭合的细节第6关的输出位置又变了这次输入被放进了a href...这样的链接属性里。这种上下文下script和事件属性都可能被限制但可以用伪协议javascript:alert(document.domain)如果href的值可控点击链接时就会执行JS。不过要注意现代浏览器对javascript:协议在href里的执行有一些限制但在靶场环境里通常还是有效的。第7关开始对引号做处理。如果双引号被转义了可以试试单引号或者不用引号。比如在属性值里如果空格没被过滤可以这样img srcx onerroralert(document.domain)这里onerror的值没有用引号包裹因为alert(document.domain)里没有空格浏览器能正确解析。如果表达式里有空格可以用/**/注释或者eval配合String.fromCharCode来规避。第8关的过滤逻辑有点意思它可能把on开头的属性都过滤了。这时候可以换其他事件比如onpointerover、onanimationstart等冷门事件。或者用svg标签配合onloadsvg onloadalert(document.domain)SVG标签在HTML5里是合法内联的onload事件在SVG加载时触发。这个向量在很多过滤规则下都能绕过因为防御者往往只盯着img和script。3.2 第9-11关JavaScript上下文里的逃逸技巧第9关是个转折点输入被放进了script代码块内部类似var name 你的输入;这种上下文下script标签和HTML事件属性都没用了因为你在JS代码里。你需要先闭合字符串再执行代码。如果双引号没被转义;alert(document.domain);//闭合了字符串;结束当前语句alert执行//注释掉后面的代码防止报错。第10关对引号做了转义但可能没处理反斜杠。这时候可以试试用\来逃逸\;alert(document.domain);//如果转义逻辑是简单地在引号前加\那么你输入\它可能变成\\反斜杠被转义引号反而逃出来了。这种转义逻辑的漏洞在真实代码里也很常见。第11关可能把alert关键字过滤了。这时候可以用编码或替代函数;eval(String.fromCharCode(97,108,101,114,116,40,49,41));//String.fromCharCode把字符码转成字符串eval执行。或者用top[alert](1)这种拼接方式绕过关键字检测。3.3 第12-14关DOM型XSS与URL编码绕过第12关开始涉及DOM型XSS。这类XSS的特点是注入的内容不经过服务器而是被前端JavaScript直接读取并写入页面。比如document.getElementById(output).innerHTML location.hash.substring(1);这种场景下你需要在URL的hash部分注入。因为hash不会发送到服务器服务端的过滤完全无效。payload可能是#img srcx onerroralert(document.domain)第13关可能对URL做了编码处理。这时候需要双重编码或者混合编码。比如的URL编码是%3C如果服务器解码一次后直接输出你可以用%253C%25是%的编码服务器解码一次变成%3C浏览器再解码一次变成。这种编码层级不匹配是绕过过滤的经典手法。第14关的过滤可能针对alert做了黑名单。这时候可以用confirm、prompt或者用console.log配合其他方式验证。靶场通常只要求弹窗所以confirm(1)和prompt(1)都是合法替代。4. 后段关卡高级绕过与综合实战4.1 第15-17关白名单、CSP与冷门向量第15关可能引入了白名单机制只允许特定标签或属性。这时候需要找白名单里的漏网之鱼。比如只允许a标签那可以试试a hrefjavascript:alert(1)。如果只允许img那就用onerror。白名单的关键是找到允许列表里能执行JS的组合。第16关可能加了简单的CSP内容安全策略。CSP通过HTTP头限制哪些源可以执行脚本。如果CSP配置不严比如允许unsafe-inline那内联脚本照样执行。如果限制了script-src可以试试JSONP端点或者AngularJS沙箱逃逸如果页面引入了Angular。靶场里的CSP通常有配置缺陷找到那个缺陷就能绕过。第17关的过滤可能针对空格、括号、引号都做了处理。这时候可以用反引号替代括号在某些上下文里或者用throw配合onerrorimg srcx onerrorthrow onerroralert,1throw会触发window.onerror如果onerror被赋值为alert就会执行alert(1)。这种利用错误处理机制的思路比较巧妙值得记住。4.2 第18-19关综合过滤下的组合拳第18关通常是前面所有过滤的集合可能同时过滤了script、on事件、引号、括号等。这时候需要组合多种技巧。比如用svgscriptSVG内的script标签配合href和xlink:href或者用math标签配合事件。一个常见的综合payload是svganimate xlink:href#x attributeNamehref valuesjavascript:alert(1) /a idxtext x20 y20click/text/a/svg这个payload利用了SVG的animate元素动态修改href属性点击文本时执行JS。它绕过了对script和常规事件属性的过滤。第19关是最终Boss过滤规则最严。这时候往往需要仔细阅读页面源码找到过滤逻辑的漏洞。比如过滤函数可能只检查了第一次出现的危险字符或者正则表达式写得有缺陷。我当时的做法是把每一关的源码都看一遍理解它的过滤逻辑然后针对性地构造payload。理解过滤逻辑比背payload重要得多因为真实环境里的过滤规则千变万化只有掌握原理才能随机应变。5. 通关之后我总结的XSS实战检查清单打完这19关我整理了一份自己的检查清单每次遇到新的XSS场景都会过一遍检查项具体动作常见绕过输出上下文看输入落在HTML的哪个位置标签间、属性内、JS内、CSS内过滤字符测试 ( )和关键字大小写、编码、嵌套、替代标签编码层级确认服务器解码几次双重编码、混合编码浏览器解析确认浏览器如何解析伪协议、SVG、MathML、冷门事件CSP策略看HTTP头里的CSP配置JSONP、unsafe-inline、配置缺陷几个我踩过的坑也分享一下。第一不要忽略document.domain和alert(1)的区别。有些靶场要求弹出特定内容用alert(1)可能不算通关要看清楚题目要求。第二浏览器版本会影响payload效果。现代浏览器对某些向量比如javascript:协议在href里有额外限制如果payload不生效先换个浏览器或者查一下兼容性。第三开发者工具是你的好朋友。每次注入后打开Elements面板看你的输入最终变成了什么HTML这比盲目试payload高效得多。最后说一个心态问题。打XSS靶场最忌讳的就是背答案。这19关的payload网上一搜一大把但如果你只是复制粘贴遇到变种就懵了。真正有价值的是理解每一关的过滤逻辑和绕过原理这样你才能在实际的安全评估中快速定位问题。我自己的习惯是每过一关都在笔记里写下这关的过滤是什么、我用了什么思路、还有没有其他解法。积累下来就是一套属于自己的XSS知识体系。另外如果你是想做防御方这19关反过来看就是一份XSS防御检查清单。每一关的过滤缺陷都对应着一种防御措施输出编码、上下文感知转义、CSP、白名单验证等。理解攻击手法是为了更好地防御这个思路在安全领域永远成立。