XSS靶场通关指南:从原理到实战的18个关卡深度解析
1. 项目概述:为什么我们需要一个XSS靶场?
如果你是一名Web安全爱好者,或者正在学习渗透测试,那么“XSS-Game”这个靶场你大概率听说过,甚至可能已经卡在了某个关卡。这个靶场,或者说一系列类似的XSS挑战平台,是安全圈里公认的“新手村”和“练功房”。它模拟了真实Web应用中可能存在的各种跨站脚本攻击场景,从最基础的反射型XSS,到需要绕过层层过滤的DOM型XSS,一共设计了18个难度递增的关卡。
我最初接触这个靶场时,也以为就是简单的弹个窗,但真正上手才发现,每一关都像是一个精心设计的谜题,考验的不仅仅是知道<script>alert(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代码块(<script>var a = ‘你的输入’;</script>)中?不同的上下文,构造Payload的方式完全不同。 - 绕过过滤:这是游戏的精髓。常见的过滤包括:删除
<script>标签、将onerror等事件属性转小写、过滤空格、用htmlspecialchars函数编码特殊字符。你的任务是找到过滤规则的“缝隙”。 - 利用链构造:有时直接执行JS不可行,需要利用现有的HTML元素或JS函数。比如,如果
<img>标签的src属性可控,可以构造src为xonerror=alert(1),利用图像加载失败来触发事件。
3. Level 1-6:基础热身与上下文感知
前六关是让你熟悉环境和最基本的Payload构造。
3.1 Level 1:最纯粹的反射型XSS
这一关通常是一个没有任何过滤的搜索框。你输入什么,它就原样输出在页面上。这是为了让你建立信心。
通关方法:直接在输入框输入经典的<script>alert(1)</script>。成功弹窗。背后的原理:你的输入被直接拼接到了HTML页面中,形成了一个新的<script>标签并被浏览器解析执行。实操要点:打开开发者工具,切换到“元素”面板,你可以看到你输入的脚本标签已经被插入到DOM树中。这一关几乎没有难度,目的是验证环境是否正常。
3.2 Level 2:HTML标签内的注入
这一关的输入可能出现在一个HTML标签内部,比如一个<h2>标签里。如果你直接输入<script>alert(1)</script>,可能会发现标签被原样显示出来,而不是执行。
通关方法:你需要先闭合掉原有的标签。例如,如果输出在<h2>你的输入</h2>里,你可以输入</h2><script>alert(1)</script><h2>。这样,你先用</h2>闭合了前面的标题标签,然后插入自己的恶意脚本,再用<h2>重新打开一个标签(避免页面结构混乱)。注意事项:观察页面源代码,精准定位你的输入被放置的上下文环境,是成功的关键。不要盲目尝试Payload。
3.3 Level 3:HTML属性内的注入(引号逃逸)
这一关,你的输入被放在了某个HTML标签的属性值里,例如一个图片的src:<img src=”你的输入”>。属性值通常被引号包围。
通关方法:目标是逃逸出属性值的范围,然后添加一个新的事件属性。Payload可以是:” onerror=”alert(1)。当它被拼接后,完整的HTML会变成:<img src=”” onerror=”alert(1)”>。首先,第一个双引号闭合了src属性的值,然后我们添加了一个onerror事件处理器,当图片加载失败(src为空)时就会触发alert(1)。核心思维:在属性值中,你需要优先考虑闭合引号,而不是插入新的标签。事件处理器(onclick,onerror,onmouseover等)是非常强大的武器。
3.4 Level 4:JavaScript代码块内的注入
这一关,你的输入被嵌入到了一段JavaScript代码中,比如:<script>var input = ‘你的输入’;</script>。这时,直接输入HTML标签是没用的,因为它在JS字符串里。
通关方法:你需要逃逸出JS字符串,然后执行代码。Payload可以是:’; alert(1);//。拼接后变成:<script>var input = ‘’; alert(1);//’;</script>。这里,单引号’闭合了前面的字符串,分号结束当前语句,然后执行我们的alert(1),最后的双斜杠//将后面原本的引号和分号注释掉,避免语法错误。经验之谈:在JS上下文中,注释符(//单行注释,/* */多行注释)是绕过过滤和修复语法错误的利器。同时,要留意使用的是单引号还是双引号,确保正确闭合。
3.5 Level 5:绕过简单的标签过滤
从这一关开始,出现了基础的过滤。例如,关卡可能会过滤或转义<script>和</script>这两个字符串。
通关方法:既然不能直接用<script>标签,我们就用其他可以执行JavaScript的HTML标签。最常见的就是利用事件处理器。例如,如果有一个可点击的链接<a href=”…”>,你可以尝试通过输入控制href,但更直接的是,如果允许你输入内容到某个标签的内部或属性,可以构造:<img src=x onerror=alert(1)>。这里,<img>标签本身是合法的HTML,过滤规则可能只针对script,从而被绕过。绕过技巧:尝试大小写混合(如<ScRiPt>),或者使用非标准的标签/事件(虽然现代浏览器支持度不一),但最可靠的还是转向使用带事件处理器的普通标签。
3.6 Level 6:利用HTML实体编码绕过
这一关可能会对尖括号< >进行HTML实体编码,即将其转换为<和>。这样,任何标签都无法被直接创建。
通关方法:我们的目光需要从标签转向协议。有些HTML属性的值,浏览器会尝试将其作为URL来处理,比如<a>标签的href,<iframe>的src,<script>的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>。即:<scr+ipt>-><script>。实操心得:面对黑名单过滤,首先要试探其过滤规则。是删除、替换还是编码?是只处理一次还是循环处理?通过输入scrscriptipt、scscriptript等变体,观察输出结果,就能反推出过滤逻辑。
4.2 Level 8:DOM型XSS初探 - 基于innerHTML的注入
这一关是典型的DOM型XSS。你的输入会通过JavaScript(比如document.getElementById(‘xxx’).innerHTML = userInput;)动态写入页面。
通关方法:即使后端对输入做了HTML编码,但innerHTML属性会将其作为HTML解析。所以,常规的HTML标签Payload仍然有效,例如<img src=x onerror=alert(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,<script>alert(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 11:Referer头注入
这一关的漏洞点不在用户直接输入,而在HTTP请求头——Referer。服务器可能将Referer头的内容未经处理就输出到了页面中。
通关方法:
- 使用Burp Suite拦截一个到该关卡页面的正常请求。
- 在Burp的Proxy或Repeater模块中,找到并修改
Referer请求头的值,将其设置为我们的XSS Payload,例如:Referer: “><script>alert(1)</script>。 - 转发请求,观察响应页面是否执行了我们的脚本。核心要点:这拓宽了我们对“输入”的理解。XSS的输入源不仅仅是表单和URL参数,还包括HTTP请求头(如
User-Agent,Cookie)、服务器端获取的其他信息(如IP地址)等。任何来自客户端、最终会被服务器端输出到HTML中的数据,都是潜在的注入点。
4.6 Level 12:User-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 src=x onerror=alert(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解码,得到<script>alert(1)</script>,然后过滤器发现并删除了<和>,变成scriptalert(1)/script,最后再进行HTML编码输出,最终无害。显然这个流程不行。 我们需要找到一个解码顺序的“缝隙”。例如,如果流程是:输入 -> 过滤<和>-> URL解码 -> 输出。那么我们可以输入%3Cscript%3E(即<script>的URL编码)。过滤器看不到<和>,所以放行。接着URL解码环节将其还原为<script>,最终成功输出。这要求我们通过反复测试,推测出服务器的处理管道。
5.3 Level 15:SVG标签与XML命名空间
当常规的HTML标签被严格过滤时,可以尝试使用SVG(可缩放矢量图形)标签。SVG是XML格式的,内嵌在HTML中,但它的某些元素和事件处理器同样可以执行JavaScript。
通关方法:构造一个SVG Payload:<svg onload=”alert(1)”></svg>。onload事件在SVG元素加载完成后触发。或者更复杂一点:<svg><script>alert(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 17:Flash嵌入与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实体编码。将
&、<、>、”、’分别转换为&、<、>、"、'。在大多数现代Web框架中,模板引擎默认会自动进行编码。 - 输出到HTML属性:同上,但尤其要注意属性值必须用引号括起来。
- 输出到JavaScript:不能使用HTML编码,而应使用JavaScript字符串编码,并将数据放入引号中。更好的做法是使用
JSON.stringify()将数据序列化为JSON,然后输出。 - 输出到URL:使用URL编码(百分号编码)。
- 输出到HTML内容:使用HTML实体编码。将
- 使用安全API:避免使用
innerHTML、outerHTML、document.write(),改用textContent或innerText。如果必须使用innerHTML,考虑使用经过严格消毒的库,或者仅用于完全受控的内容。 - 内容安全策略:部署CSP(Content Security Policy)HTTP头。这是一个强大的深度防御措施,可以告诉浏览器只允许加载和执行来自特定来源的脚本、样式等资源,从根本上杜绝内联脚本和未经授权的资源加载。例如,一个严格的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应用环境更加复杂,框架、库、WAF(Web应用防火墙)都会引入新的变量。但万变不离其宗,核心永远是:数据从哪里来,到哪里去,中间经历了怎样的处理?把握住这个数据流,你就能像解开靶场关卡一样,层层剥开真实世界中的安全谜题。记住,最好的学习方式不是记住这18个Payload,而是理解每一关背后迫使你思考的那个“为什么”。当你下次面对一个输入框,能下意识地在脑海里构建出数据流的完整图谱时,你就真正出师了。