1. 项目概述为什么我们需要一个XSS靶场如果你是一名Web安全爱好者或者正在学习渗透测试那么“XSS-Game”这个靶场你大概率听说过甚至可能已经卡在了某个关卡。这个靶场或者说一系列类似的XSS挑战平台是安全圈里公认的“新手村”和“练功房”。它模拟了真实Web应用中可能存在的各种跨站脚本攻击场景从最基础的反射型XSS到需要绕过层层过滤的DOM型XSS一共设计了18个难度递增的关卡。我最初接触这个靶场时也以为就是简单的弹个窗但真正上手才发现每一关都像是一个精心设计的谜题考验的不仅仅是知道scriptalert(1)/script这个Payload更是对浏览器解析、JavaScript执行、HTML编码以及各种过滤绕过技巧的深刻理解。通关这个靶场你获得的不仅仅是一个“通关”的标签而是一套系统的、实战化的XSS漏洞挖掘与利用思维。这对于理解现代Web应用的安全边界、编写更安全的代码甚至是参加CTF比赛都有着不可估量的价值。今天我就结合自己通关的经验以及这些年踩过的坑为你拆解这18个关卡目标是让你不仅能“过”更能“懂”。2. 通关前准备环境、工具与核心思维在开始“闯关”之前磨刀不误砍柴工。正确的环境和思维模式能让你事半功倍。2.1 靶场环境搭建与访问最经典的XSS-Game由Google提供但原版可能访问不便。好在社区有大量镜像和复现版本。你可以直接搜索“XSS Game”或“XSS挑战平台”找到在线的版本。我个人更推荐在本地搭建一个可控的环境比如使用Docker快速部署一个包含多种漏洞的集成靶场如Pikachu、DVWA其中就包含了XSS-Game的关卡。这样做的好处是你可以随意查看后端源码、修改配置、反复测试而不受网络限制。工具准备清单浏览器Chrome或Firefox的最新版。它们的开发者工具F12是我们的主战场。代理工具Burp Suite Community版。它不仅是抓包改包神器其Repeater重放和Intruder爆破模块在测试复杂Payload时极其有用。文本编辑器用于编写和测试Payload。推荐VS Code或Sublime Text。浏览器扩展如“EditThisCookie”用于方便地操作Cookie在某些关卡会用到。注意使用Burp Suite时务必配置好浏览器代理并安装Burp的CA证书否则无法拦截HTTPS流量。这是一个新手常踩的坑如果遇到页面无法加载或证书错误请先检查代理和证书配置。2.2 理解XSS的三种基本类型通关的核心在于对症下药。你必须清楚每一关的漏洞属于哪种类型因为利用方式天差地别。反射型XSS攻击Payload“反射”在响应页面中。通常出现在搜索框、错误消息等地方。特点Payload通过一次请求如URL参数、表单提交并立即在返回的页面中执行。它需要诱导用户点击一个恶意链接。存储型XSS攻击Payload被“存储”在服务器端如数据库当其他用户访问特定页面时触发。常见于论坛评论、用户昵称、留言板。特点一次注入影响所有查看该内容的用户危害最大。DOM型XSS漏洞的根源在于前端JavaScript代码不安全地操作了DOM。攻击Payload并不出现在服务器响应中而是通过客户端的JS代码写入DOM并执行。特点完全在浏览器端完成查看网页源代码可能找不到Payload必须在开发者工具的“元素”面板或动态调试中才能发现。2.3 核心测试与绕过思维不要死记硬背Payload。靶场的价值在于训练你的思维。面对一个输入点你应该像侦探一样思考信息收集输入一些特殊字符如 “ ‘ 观察输出在哪里、被如何处理的。是被直接输出被HTML编码了被删除了还是被转义了上下文判断你的输入最终出现在HTML的哪个位置是在普通的HTML标签内如div、HTML属性里如input value”你的输入”、还是在JavaScript代码块scriptvar a ‘你的输入’;/script中不同的上下文构造Payload的方式完全不同。绕过过滤这是游戏的精髓。常见的过滤包括删除script标签、将onerror等事件属性转小写、过滤空格、用htmlspecialchars函数编码特殊字符。你的任务是找到过滤规则的“缝隙”。利用链构造有时直接执行JS不可行需要利用现有的HTML元素或JS函数。比如如果img标签的src属性可控可以构造src为xonerroralert(1)利用图像加载失败来触发事件。3. Level 1-6基础热身与上下文感知前六关是让你熟悉环境和最基本的Payload构造。3.1 Level 1最纯粹的反射型XSS这一关通常是一个没有任何过滤的搜索框。你输入什么它就原样输出在页面上。这是为了让你建立信心。通关方法直接在输入框输入经典的scriptalert(1)/script。成功弹窗。背后的原理你的输入被直接拼接到了HTML页面中形成了一个新的script标签并被浏览器解析执行。实操要点打开开发者工具切换到“元素”面板你可以看到你输入的脚本标签已经被插入到DOM树中。这一关几乎没有难度目的是验证环境是否正常。3.2 Level 2HTML标签内的注入这一关的输入可能出现在一个HTML标签内部比如一个h2标签里。如果你直接输入scriptalert(1)/script可能会发现标签被原样显示出来而不是执行。通关方法你需要先闭合掉原有的标签。例如如果输出在h2你的输入/h2里你可以输入/h2scriptalert(1)/scripth2。这样你先用/h2闭合了前面的标题标签然后插入自己的恶意脚本再用h2重新打开一个标签避免页面结构混乱。注意事项观察页面源代码精准定位你的输入被放置的上下文环境是成功的关键。不要盲目尝试Payload。3.3 Level 3HTML属性内的注入引号逃逸这一关你的输入被放在了某个HTML标签的属性值里例如一个图片的srcimg src”你的输入”。属性值通常被引号包围。通关方法目标是逃逸出属性值的范围然后添加一个新的事件属性。Payload可以是” onerror”alert(1)。当它被拼接后完整的HTML会变成img src”” onerror”alert(1)”。首先第一个双引号闭合了src属性的值然后我们添加了一个onerror事件处理器当图片加载失败src为空时就会触发alert(1)。核心思维在属性值中你需要优先考虑闭合引号而不是插入新的标签。事件处理器onclick,onerror,onmouseover等是非常强大的武器。3.4 Level 4JavaScript代码块内的注入这一关你的输入被嵌入到了一段JavaScript代码中比如scriptvar input ‘你的输入’;/script。这时直接输入HTML标签是没用的因为它在JS字符串里。通关方法你需要逃逸出JS字符串然后执行代码。Payload可以是’; alert(1);//。拼接后变成scriptvar input ‘’; alert(1);//’;/script。这里单引号’闭合了前面的字符串分号结束当前语句然后执行我们的alert(1)最后的双斜杠//将后面原本的引号和分号注释掉避免语法错误。经验之谈在JS上下文中注释符//单行注释/* */多行注释是绕过过滤和修复语法错误的利器。同时要留意使用的是单引号还是双引号确保正确闭合。3.5 Level 5绕过简单的标签过滤从这一关开始出现了基础的过滤。例如关卡可能会过滤或转义script和/script这两个字符串。通关方法既然不能直接用script标签我们就用其他可以执行JavaScript的HTML标签。最常见的就是利用事件处理器。例如如果有一个可点击的链接a href”…”你可以尝试通过输入控制href但更直接的是如果允许你输入内容到某个标签的内部或属性可以构造img srcx onerroralert(1)。这里img标签本身是合法的HTML过滤规则可能只针对script从而被绕过。绕过技巧尝试大小写混合如ScRiPt或者使用非标准的标签/事件虽然现代浏览器支持度不一但最可靠的还是转向使用带事件处理器的普通标签。3.6 Level 6利用HTML实体编码绕过这一关可能会对尖括号 进行HTML实体编码即将其转换为lt;和gt;。这样任何标签都无法被直接创建。通关方法我们的目光需要从标签转向协议。有些HTML属性的值浏览器会尝试将其作为URL来处理比如a标签的hrefiframe的srcscript的src等。如果我们可以控制这些属性可以尝试使用javascript:协议。例如构造一个链接a href”javascript:alert(1)”Click me/a。用户点击这个链接时就会执行JS代码。关键点检查是否有哪个标签的属性值未被充分过滤且浏览器会对其执行URL解析。javascript:协议是一个强大的备用方案。注意现代浏览器对javascript:协议在部分上下文如iframe src中可能有更严格的限制。4. Level 7-12过滤绕过与思维转换中级关卡开始引入更复杂的过滤机制需要更巧妙的绕过技巧。4.1 Level 7字符串替换过滤及其缺陷这一关可能采用简单的字符串替换来过滤关键词例如将script替换为空字符串。如果你输入script它会被处理成。通关方法利用替换逻辑的缺陷。如果它是简单的一次性替换我们可以使用“嵌套”或“拼接”的技巧。例如输入scrscriptipt。当过滤器删除中间的script后剩下的字符正好又组合成了一个新的script。即script-script。实操心得面对黑名单过滤首先要试探其过滤规则。是删除、替换还是编码是只处理一次还是循环处理通过输入scrscriptipt、scscriptript等变体观察输出结果就能反推出过滤逻辑。4.2 Level 8DOM型XSS初探 - 基于innerHTML的注入这一关是典型的DOM型XSS。你的输入会通过JavaScript比如document.getElementById(‘xxx’).innerHTML userInput;动态写入页面。通关方法即使后端对输入做了HTML编码但innerHTML属性会将其作为HTML解析。所以常规的HTML标签Payload仍然有效例如img srcx onerroralert(1)。与反射型的区别你需要打开开发者工具的“元素”面板而不是查看“网页源代码”。因为通过innerHTML添加的内容不会出现在初始的HTML源码里只存在于动态构建的DOM树中。这是判断是否为DOM型XSS的重要标志。4.3 Level 9利用javascript:协议与URL验证这一关可能要求你输入一个URL并且后端会检查是否以http://或https://开头。我们的目标是让这个URL最终执行JS。通关方法我们可以构造一个以合法协议开头但后续部分能执行代码的Payload。例如javascript:alert(1)//http://example.com。当过滤器检查字符串开头是否有http时它看到了javascript:这可能会通过检查取决于检查逻辑的严密性。而//在JavaScript中是单行注释其后的http://example.com会被注释掉不会影响alert(1)的执行。另一种思路如果检查非常严格只允许http://或https://开头可以尝试利用数据协议data:text/html,scriptalert(1)/script。但数据协议通常会被更严格地限制。4.4 Level 10隐藏参数与事件触发这一关的注入点可能不在明显的输入框而是隐藏在URL参数、表单隐藏域input type”hidden”中需要通过事件如onclick、onmouseover来触发。通关方法寻找注入点使用Burp Suite拦截所有请求观察每一个参数。或者仔细查看页面HTML源码寻找所有可能由你控制的值。构造触发事件如果输入被放入了一个隐藏输入框的value属性如input type”hidden” value”你的输入”你可以尝试修改其类型为可见或者更常见的是在该元素或其父元素上添加一个事件处理器。例如如果页面有一个按钮你可以尝试通过输入” onclick”alert(1)来污染其onclick事件如果该按钮的某个属性可控。排查技巧对于DOM型或隐藏式的XSS在开发者工具的“控制台”中运行document.querySelectorAll(‘[value*”test”]’)之类的命令可以帮助你快速定位你的输入被放置在DOM的哪个角落。4.5 Level 11Referer头注入这一关的漏洞点不在用户直接输入而在HTTP请求头——Referer。服务器可能将Referer头的内容未经处理就输出到了页面中。通关方法使用Burp Suite拦截一个到该关卡页面的正常请求。在Burp的Proxy或Repeater模块中找到并修改Referer请求头的值将其设置为我们的XSS Payload例如Referer: “scriptalert(1)/script。转发请求观察响应页面是否执行了我们的脚本。核心要点这拓宽了我们对“输入”的理解。XSS的输入源不仅仅是表单和URL参数还包括HTTP请求头如User-Agent,Cookie、服务器端获取的其他信息如IP地址等。任何来自客户端、最终会被服务器端输出到HTML中的数据都是潜在的注入点。4.6 Level 12User-Agent头注入与上一关类似但注入点换成了User-Agent头。User-Agent是浏览器标识自己的字符串同样可以被恶意篡改。通关方法操作步骤与Level 11完全相同只是修改的对象从Referer变成了User-Agent。Payload可以是一样的。深入思考这两关揭示了“存储型”XSS的另一种形式——基于日志的XSS。如果管理员查看的日志分析页面不加过滤地显示了访问日志中的User-Agent或Referer字段那么攻击者只需要访问一次网站就能在日志中植入恶意代码当管理员查看日志时触发攻击。这种攻击隐蔽性极高。5. Level 13-18高级技巧与综合挑战最后六关通常融合了多种过滤、编码和奇怪的上下文是对你综合能力的终极考验。5.1 Level 13基于Cookie的注入这一关的输入源是Cookie。服务器端读取了某个Cookie的值并将其输出到页面上。通关方法使用浏览器插件如EditThisCookie或开发者工具Application - Storage - Cookies直接修改当前网站下的特定Cookie值。将值设置为XSS Payload例如img srcx onerroralert(1)。刷新页面观察Payload是否执行。难点与绕过难点在于发现注入点。你需要仔细审查页面看哪些内容看起来像是“个性化”的并且可能来源于Cookie。此外Cookie值通常会有URL编码但服务器在输出前会解码所以直接输入Payload即可。注意如果Cookie设置了HttpOnly属性客户端JavaScript将无法读写它但这种由服务端输出到HTML的情况HttpOnly无法防护。5.2 Level 14双重编码与解码流这一关可能对输入进行了多次编码或者在不同的处理阶段进行了不同的编解码操作。例如输入先被HTML实体编码然后在某个环节又被URL解码。通关方法你需要像一个编译器一样思考数据的流动。假设过滤流程是输入 - URL解码 - 过滤和- HTML编码 - 输出。那么你可以输入经过URL编码的Payload%3Cscript%3Ealert(1)%3C/script%3E。服务器收到后先进行URL解码得到scriptalert(1)/script然后过滤器发现并删除了和变成scriptalert(1)/script最后再进行HTML编码输出最终无害。显然这个流程不行。 我们需要找到一个解码顺序的“缝隙”。例如如果流程是输入 - 过滤和- URL解码 - 输出。那么我们可以输入%3Cscript%3E即script的URL编码。过滤器看不到和所以放行。接着URL解码环节将其还原为script最终成功输出。这要求我们通过反复测试推测出服务器的处理管道。5.3 Level 15SVG标签与XML命名空间当常规的HTML标签被严格过滤时可以尝试使用SVG可缩放矢量图形标签。SVG是XML格式的内嵌在HTML中但它的某些元素和事件处理器同样可以执行JavaScript。通关方法构造一个SVG Payloadsvg onload”alert(1)”/svg。onload事件在SVG元素加载完成后触发。或者更复杂一点svgscriptalert(1)/script/svg。注意在SVG内部的script标签可能需要指定正确的命名空间但现代浏览器通常能正确解析。为什么有效许多XSS过滤器只针对常见的HTML标签div,img,script和事件可能会忽略SVG这个相对小众的向量。这体现了攻击面扩大的思想。5.4 Level 16利用CSS表达式IE特性或现代CSS注入这是一道“历史题”或“拓展题”。在旧版本的Internet Explorer中CSS的expression()属性可以执行JavaScript例如style”width: expression(alert(1))”。虽然现代浏览器已不支持但靶场可能模拟了这个环境来拓宽知识面。现代变种在现代浏览器中CSS注入也可能导致问题例如通过import引入外部样式表或者利用CSS的url()属性结合某些浏览器的特性如旧的IE执行代码但难度和通用性很低。这一关主要是为了让你知道XSS的载体不仅仅是HTML和JS样式表在某些特定条件下也可能成为攻击向量。通关思路如果关卡环境模拟IE尝试div style”width: expression(alert(1))”test/div。否则需要查看关卡提示寻找其他非主流的执行路径。5.5 Level 17Flash嵌入与allowScriptAccess这一关涉及已淘汰的Flash技术但作为安全历史的一部分仍有教育意义。Flash文件SWF可以通过embed或object标签嵌入网页并可以通过参数与JavaScript交互。核心参数allowScriptAccess参数控制Flash是否能够访问页面中的JavaScript。如果被设置为always或sameDomain且Flash文件本身存在缺陷或被攻击者控制就可能引发XSS。通关方法通常你需要结合一个可控制的Flash文件地址movie或src参数以及allowScriptAccess参数。例如如果src参数可控可以指向一个恶意构造的SWF文件该文件包含调用alert(1)的ActionScript代码。或者通过FlashVars参数向SWF传递恶意数据。由于Flash已死此关更多是概念性理解。5.6 Level 18综合挑战与白盒审计最后一关往往是“大杂烩”可能是一个小型应用你需要结合前面所有技巧甚至需要查看前端JS源码白盒才能找到隐藏的漏洞点。它可能涉及多个输入点需要找到那个过滤最弱或处理逻辑有问题的点。客户端模板渲染使用如AngularJS旧版本有沙箱逃逸问题、Vue.js{{}}模板在不当配置下可能导致XSS等框架的客户端渲染漏洞。不安全的JavaScript函数如eval()、setTimeout()、innerHTML、document.write()等如果其参数完全或部分可控就可能产生DOM型XSS。JSONP回调函数注入如果动态创建script标签加载JSONP接口且回调函数名可控可能造成XSS。通关策略全面侦察用Burp爬取所有链接测试所有参数。代码分析仔细查看页面引用的所有JavaScript文件搜索innerHTML、document.write、eval、location.hash、postMessage等危险函数和敏感源。链式利用可能单个点无法直接弹窗但A点的输出会成为B点的输入形成攻击链。利用框架特性如果发现使用了老版本的前端框架去搜索该版本已知的XSS攻击向量。6. 实战后的思考从通关到防御通关所有关卡后你获得的是一套攻击者的思维。但更重要的是你要学会用这种思维来构建防御。6.1 开发者如何避免XSS原则绝不信任用户输入。所有来自外部的数据用户输入、URL参数、HTTP头、第三方API数据在输出到HTML、JavaScript、CSS或URL上下文之前都必须经过处理。输出编码输出到HTML内容使用HTML实体编码。将、、、”、’分别转换为amp;、lt;、gt;、quot;、#x27;。在大多数现代Web框架中模板引擎默认会自动进行编码。输出到HTML属性同上但尤其要注意属性值必须用引号括起来。输出到JavaScript不能使用HTML编码而应使用JavaScript字符串编码并将数据放入引号中。更好的做法是使用JSON.stringify()将数据序列化为JSON然后输出。输出到URL使用URL编码百分号编码。使用安全API避免使用innerHTML、outerHTML、document.write()改用textContent或innerText。如果必须使用innerHTML考虑使用经过严格消毒的库或者仅用于完全受控的内容。内容安全策略部署CSPContent Security PolicyHTTP头。这是一个强大的深度防御措施可以告诉浏览器只允许加载和执行来自特定来源的脚本、样式等资源从根本上杜绝内联脚本和未经授权的资源加载。例如一个严格的CSP可以禁止unsafe-inline和unsafe-eval。输入验证在接收端对输入数据的格式、长度、类型进行严格校验。但这只能作为辅助手段不能替代输出编码因为验证规则可能被绕过。6.2 作为安全测试者如何系统化地挖掘XSS手动测试对于关键功能点登录、搜索、评论、个人资料编辑按照本文所述的思维系统性地测试每一个输入点。工具辅助使用Burp Suite的Active Scan或专门的XSS扫描工具如XSStrike、xsser进行自动化探测。但工具只能发现最明显的问题高级漏洞仍需手动。代码审计如果有可能直接阅读前端JavaScript代码和后端处理逻辑这是发现DOM型XSS和复杂逻辑漏洞的最有效方式。寻找那些将可控数据传递给危险函数的地方。扩大攻击面不要只盯着表单和URL。测试所有的HTTP头、Flash/PDF/图片等文件上传功能、WebSocket消息、服务器端返回的错误信息等。通关XSS-Game只是一个开始。真实的Web应用环境更加复杂框架、库、WAFWeb应用防火墙都会引入新的变量。但万变不离其宗核心永远是数据从哪里来到哪里去中间经历了怎样的处理把握住这个数据流你就能像解开靶场关卡一样层层剥开真实世界中的安全谜题。记住最好的学习方式不是记住这18个Payload而是理解每一关背后迫使你思考的那个“为什么”。当你下次面对一个输入框能下意识地在脑海里构建出数据流的完整图谱时你就真正出师了。