资讯中心

XSS-labs靶场通关指南:从原理到实战的20关Web安全进阶

📅 2026/6/24 18:55:30
XSS-labs靶场通关指南:从原理到实战的20关Web安全进阶
1. 项目概述为什么选择XSS-labs作为你的第一块磨刀石如果你刚接触Web安全或者对跨站脚本攻击XSS这个概念还停留在“听说过”的阶段那么找一个好的靶场来练手是比看一百篇理论文章都更有效的学习方法。在众多靶场中XSS-labs以其清晰的关卡设计、由浅入深的难度梯度和对XSS核心原理的精准覆盖成为了无数安全新手的“启蒙老师”。这个靶场模拟了20种不同的场景每一关都像一道精心设计的谜题你需要利用对HTML、JavaScript以及浏览器解析逻辑的理解去找到那个“注入点”并成功执行任意脚本。我最初接触它时也走了不少弯路比如过分依赖自动化工具或者死记硬背Payload而不知其所以然。后来才发现XSS-labs的精髓不在于“通关”而在于迫使你去思考代码是如何被嵌入的浏览器是如何解析和渲染的过滤规则是如何被绕过的这20关几乎涵盖了反射型、存储型、DOM型XSS中最常见的触发场景和绕过技巧。通过手动攻克每一关你能建立起对XSS漏洞最直观的肌肉记忆。今天我就结合自己多次通关的经验以及带新人时他们最容易卡壳的地方为你拆解这20关的突破思路。我们的目标不是给出标准答案而是让你理解每一关背后的“为什么”从而具备独立分析和构造Payload的能力。2. 核心思路与前置知识磨刀不误砍柴工在开始“闯关”之前我们必须统一思想明确XSS攻击的本质和通关的核心方法论。盲目尝试只会事倍功半。2.1 XSS攻击的核心逻辑与三种类型XSS的全称是Cross-Site Scripting为了和CSS区分而简称XSS。其核心攻击逻辑是攻击者将恶意脚本代码注入到可信的网页中当其他用户浏览该网页时嵌入的恶意代码会被浏览器执行从而在用户不知情的情况下窃取Cookie、会话令牌甚至进行钓鱼、挂马等操作。根据恶意代码的存储和触发位置主要分为三类反射型XSS恶意脚本来自当前HTTP请求。最常见的是攻击者构造一个包含恶意代码的URL诱骗用户点击。服务器接收到请求后未经过滤便将恶意代码“反射”回用户的浏览器页面中执行。XSS-labs的前面大部分关卡都是这种类型。存储型XSS恶意脚本被永久存储在服务器端如数据库、文件系统。当其他用户访问某个页面如论坛帖子、评论列表时服务器从存储中取出并返回恶意脚本导致其在用户浏览器执行。危害更大因为所有访问者都可能中招。DOM型XSS漏洞的根源在于前端的JavaScript代码。攻击者通过修改页面的DOM文档对象模型环境诱使客户端脚本执行非预期的操作。整个过程不涉及与服务器的交互恶意代码不经过服务器完全在浏览器端完成。XSS-labs的关卡设计巧妙地将这三种类型的原理融入其中你需要根据页面反馈和代码逻辑来判断属于哪一种并采取相应的攻击策略。2.2 通关必备的工具与浏览器设置工欲善其事必先利其器。以下是我实战中离不开的几样东西浏览器开发者工具这是你最重要的“眼睛”。主要使用两个面板元素面板查看页面渲染后的HTML结构。当你注入的代码被插入后可以在这里看到它最终是如何被呈现的。特别注意script标签是否被创建、属性值是否被闭合。网络面板查看浏览器发送的HTTP请求和接收的响应。对于反射型XSS你可以在这里清晰地看到你输入的Payload是如何被发送以及服务器返回了什么。这对于分析过滤和编码规则至关重要。Burp Suite 或 HackBar 浏览器插件用于方便地构造和发送HTTP请求。Burp Suite功能强大可以拦截、修改、重放请求是专业测试的标配。对于新手浏览器插件版的HackBar更为轻量快捷可以直接在浏览器地址栏下方构造GET/POST请求非常适合XSS-labs这类靶场练习。一个简单的HTTP服务器用于接收被窃取的数据如Cookie。当你的Payload成功执行后通常需要证明其危害性比如将用户的Cookie发送到你的服务器。你可以用Python快速搭建一个python3 -m http.server 8000。这样所有发送到http://你的IP:8000/的请求都会被记录。浏览器安全策略调整为了更清晰地观察漏洞效果有时需要暂时调整浏览器设置。例如在Chrome中可以启动时加上参数--disable-xss-auditor旧版本或注意现代Chrome的XSS Auditor已被移除主要依赖CSP。练习时也可以暂时禁用CSP内容安全策略但这仅限本地靶场环境真实测试中绝对不可行。注意所有工具和技巧仅用于授权的安全测试和学习环境如本地搭建的XSS-labs。未经授权对他人的网站进行测试是非法行为。2.3 通用测试流程与思维模型面对每一关建议遵循以下步骤形成条件反射信息收集正常输入一串特征明显的测试字符串如test或“。提交后立刻用开发者工具的“元素面板”查看这个字符串被放在了HTML的什么位置。是被放在标签内如div你的输入/div还是标签的属性里如input value“你的输入”或者是JavaScript代码段中如scriptvar a ‘你的输入;/script位置决定了你的攻击方式。上下文分析确定输入点所处的“上下文”。是HTML正文是HTML标签属性单引号还是双引号包裹是JavaScript字符串还是URL地址不同的上下文需要不同的闭合和绕过方式。尝试闭合与构造根据上下文尝试用“’/script等符号去闭合当前的语法环境为插入恶意脚本如scriptalert(1)/script或onerroralert(1)腾出空间。观察过滤与编码如果简单的Payload失败了查看网络面板的响应或页面源码看你的输入哪些部分被修改了。是被删除了被编码了如变成lt;还是被替换了根据过滤规则思考绕过方法。利用事件与协议当script标签被过滤时就要转向利用HTML标签的事件属性如onmouseover,onload,onerror或支持JavaScript协议的属性如a hrefjavascript:alert(1)img src1 onerroralert(1)。3. 前10关基础语法闭合与事件触发这10关是基本功训练核心是理解HTML的语法结构和如何闭合它们。3.1 第1-3关直接的标签注入与简单属性闭合第1关通常是最简单的没有任何过滤。你的输入直接出现在HTML正文中。你只需要构造一个完整的script标签即可。例如在输入框提交scriptalert(document.domain)/script。这里用document.domain代替简单的数字可以更直观地证明你控制了当前域下的脚本执行。第2关输入被放在了HTML标签的属性值里比如一个input标签的value属性。查看元素会发现类似input type“text” value“你输入的内容”。这里的难点是你被关在双引号里面。你需要先闭合双引号然后引入事件处理器或新标签。Payload“scriptalert(1)/script。这个Payload的分解是“先闭合当前属性值的双引号再闭合input标签本身。scriptalert(1)/script然后在原标签后面插入新的脚本标签。 这样最终的HTML就变成了input type“text” value“”scriptalert(1)/script“。注意最后多了一个孤立的“但不影响脚本执行。第3关情况与第2关类似但属性值是用单引号包裹的。查看元素input type‘text’ value‘你输入的内容’。这时你需要用单引号来闭合。Payload‘scriptalert(1)/script。原理同上。实操心得很多新手在这里会困惑为什么有时候要用“有时候用’。秘诀就是看源码永远不要猜用开发者工具看一眼输入值被什么符号包裹着你就用什么符号去闭合。这是最基本也是最关键的一步。3.4 第4-6关利用事件处理器与伪协议当script标签被过滤或上下文不适合插入完整标签时我们就需要利用其他方式触发JavaScript执行。第4关输入可能被放在诸如input或button的value属性中且和可能被过滤。这时我们不必闭合标签而是利用该标签本身支持的事件属性。例如如果输入点在一个input标签内我们可以尝试构造“ onmouseover“alert(1)。注意这里有一个空格。这个Payload的意图是“闭合前面的双引号。空格分隔属性。onmouseover“alert(1)添加一个新的onmouseover事件属性其值为alert(1)。由于我们没有闭合最后的双引号它会“借用”原来标签结束的引号。最终生成input ... value“” onmouseover“alert(1)”。当鼠标滑过这个输入框时就会触发弹窗。第5关可能会过滤onmouseover这类常见事件但可能漏掉一些不那么常见的事件或者过滤了script关键字但没过滤javascript:伪协议。我们可以尝试使用a标签“a href“javascript:alert(1)”click/a。或者如果输入点在script标签的某个字符串变量中你需要用/script先闭合前面的script标签再插入新的。第6关通常开始引入大小写绕过。如果系统过滤了script但只是进行简单的大小写敏感匹配那么ScRiPt就可能绕过。PayloadScRiPtalert(1)/ScRiPt。这一关是让你建立“过滤规则可能不完善”的意识。3.5 第7-10关双写绕过与编码初探从这里开始靶场会引入更积极的过滤机制你需要学会“欺骗”过滤器。第7关可能会直接删除script这个关键词。例如你输入scriptalert(1)/script服务器处理后变成了alert(1)/。对付这种“删除”策略一个经典的方法是双写绕过。Payloadscrscriptiptalert(1)/scrscriptipt。当过滤器删除中间的script后剩下的字符正好又组合成了一个新的script标签。第8关可能会将输入内容作为某个标签的属性值并过滤空格和javascript:关键字。例如你需要在一个a标签的href属性中注入。如果空格被过滤你可以用Tab的URL编码%09或者换行符的编码来分隔属性。Payloadjavascript:alert(1)可能被过滤但可以尝试使用HTML实体编码进行混淆。例如将javascript:编码为#106;#97;#118;#97;#115;#99;#114;#105;#112;#116;:。但要注意浏览器在解析href属性时会对HTML实体进行解码。所以你需要确保编码后的字符串在浏览器端能被正确解码为javascript:。第9关可能会检查输入内容是否包含http://要求看起来像一个合法的链接。这时你需要将恶意代码“伪装”成一个合法链接。Payloadjavascript:alert(1)//http://xxx.com。//在JavaScript中是单行注释它会让后面的http://被注释掉从而不影响前面的javascript:协议执行。第10关通常是一个综合练习可能隐藏了多个输入点或者需要通过查看网页源码发现隐藏的input表单。你需要利用前面积累的所有技巧可能结合type“hidden”的输入框通过修改其value和type属性或者利用form的onsubmit事件来触发XSS。这一关的关键是全面的信息收集不要只盯着显眼的输入框。4. 中阶挑战第11-15关DOM型XSS与闭合技巧进阶从第11关开始XSS-labs往往会引入更多DOM操作和复杂的上下文环境挑战你对JavaScript和浏览器解析顺序的理解。4.1 第11关Referer头注入与HTTP头XSS这一关通常不通过页面表单输入而是通过修改HTTP请求头来触发XSS。常见的是Referer头或User-Agent头。服务器可能会将这些头信息未经处理就直接输出到页面的某个部分比如一段注释或某个JavaScript变量中。攻击步骤使用Burp Suite或浏览器插件拦截你对目标页面的请求。在拦截的请求中找到Referer头或其他指定的头字段。将其值修改为你的XSS Payload例如Referer: “scriptalert(document.domain)/script。放行请求观察页面是否弹窗。注意事项HTTP头注入的XSS属于反射型但触发方式更隐蔽。它要求攻击者能诱骗用户通过一个可控的代理发出请求或者结合其他漏洞如CRLF注入来实现。在实战中检查应用是否将不可信的数据从HTTP头回显到页面是一个重要的测试点。4.2 第12关User-Agent头注入与过滤绕过原理与第11关类似但可能对script等关键词进行了过滤。你需要尝试使用其他标签和事件。例如将User-Agent头设置为“ onfocus“alert(1) autofocus“。这里构造了一个自动获取焦点的元素当页面加载时onfocus事件会自动触发。或者使用img src1 onerroralert(1)。关键在于观察服务器对头的处理方式是直接嵌入HTML还是先做了处理。4.3 第13关Cookie注入与输入点挖掘这一关可能会检查HTTP请求中的Cookie值并将其输出。攻击方式与头注入类似但Cookie通常由服务端设置客户端直接修改可能不会生效因为服务端可能不信任客户端传来的Cookie。但有些设计不当的应用可能会根据请求中的某个Cookie参数来动态生成页面内容。你需要仔细分析页面逻辑或者查看源码寻找类似document.cookie或服务器端模板变量输出Cookie的地方。技巧如果Cookie值被输出到script标签内的一个字符串变量中如var userInput ‘你的cookie值’;那么你需要先闭合单引号然后插入代码。Payload‘;alert(1);//。这样会得到var userInput ‘’;alert(1);//’;//注释掉了后面的内容。4.4 第14关DOM XSS - 利用document.writeDOM型XSS的典型场景。页面中可能有一段JavaScript代码从URL的片段标识符#后面的部分或查询参数中获取数据然后通过document.write()或innerHTML直接写入页面。例如源码中可能有var input window.location.hash.substring(1); document.write(“p你输入的是” input “/p“);攻击者可以构造URLhttp://靶场地址/level14.html#img src1 onerroralert(1)。当受害者访问此URL时window.location.hash的值是#img src1 onerroralert(1)substring(1)去掉#后剩下的字符串被直接拼接进HTML并写入文档导致img标签被解析其onerror事件触发。核心思路找到那些从location.search、location.hash、document.referrer等来源获取数据并直接传递给innerHTML、outerHTML、document.write()、eval()等“危险”函数的代码点。4.5 第15关DOM XSS - 利用eval或innerHTML这一关是第14关的变种或强化。可能使用eval()函数或者通过innerHTML赋值并且可能对输入内容进行了一些编码或过滤。例如var data decodeURIComponent(window.location.search.split(‘’)[1]); document.getElementById(‘someDiv’).innerHTML data;这里从URL参数获取值进行URL解码后直接设置innerHTML。你可以构造?dataimg src1 onerroralert(1)。如果参数值被URL编码了你可能需要输入编码后的形式或者依赖浏览器/服务器的自动解码。绕过技巧如果过滤了和可以尝试使用JavaScript模板字符串或者通过String.fromCharCode动态生成标签。例如img可以写成img但注意在innerHTML上下文中浏览器会解析实体所以有时需要双重编码或者寻找不依赖尖括号的注入方式如利用已有标签的属性。5. 高阶绕过第16-20关编码、特殊上下文与综合技巧最后几关模拟了现实中更严格的过滤环境需要综合运用多种绕过技术。5.1 第16关利用HTML实体编码与JavaScript Unicode编码这一关可能会对输入中的特殊字符进行HTML实体编码比如将转成lt;将转成gt;。如果这种编码发生在服务端并且你的输入被放在HTML标签属性值中那么script标签就无法直接使用了。但是如果输入被放在script标签内部的字符串中情况就不同了。例如script var x ‘用户输入’; // 后续可能用eval(x)或document.write(x) /script如果服务器对用户输入进行了HTML实体编码变成了#60;script#62;那么在script标签内它只是一个字符串不会被浏览器解析为HTML标签。然而如果这个字符串后来被传递给eval()或者作为innerHTML的值那么问题就来了。eval()执行的是JavaScript字符串而innerHTML赋值时浏览器会解析HTML实体。攻击思路寻找二次输出点也许编码后的字符串会被输出到另一个不编码的上下文。利用事件处理器即使尖括号被编码在HTML属性中事件处理器如onmouseover仍然可以工作只要它所在的标签本身是存在的。例如如果输入被放在一个input的value里且被编码你无法闭合标签。但如果你能注入一个空格和事件属性如onmouseoveralert(1)即使后面的值被编码只要属性名onmouseover被正确识别它依然可能执行。但前提是属性名本身没有被过滤或编码。使用JavaScript Unicode编码在script标签内部你可以使用\u0061\u006c\u0065\u0072\u0074(1)来表示alert(1)。如果服务器只过滤了关键词但允许Unicode字符这可能有效。5.2 第17关SVG标签与事件绕过当常规的HTML标签和事件被严格过滤时可以尝试使用SVG可缩放矢量图形标签。SVG是XML格式内嵌在HTML中并且支持HTML的事件处理器。一个典型的SVG XSS Payloadsvg onloadalert(1)或者更复杂一点svgscriptalert(1)/script/svg有些过滤器可能只针对常见的img、div等HTML标签而忽略了svg。此外SVG内部可以包含script标签这提供了另一种执行脚本的途径。5.3 第18关利用Flash/ActionScript历史技巧或HTML5新特性这是一关比较有历史感的关卡可能涉及已淘汰的技术但其思路仍有借鉴意义。早期有些XSS通过嵌入恶意的Flash文件.swf并利用ActionScript的ExternalInterface.call()来调用页面JavaScript。如今Flash已被淘汰但思路可以转换。现代绕过可能包括details标签的ontoggle事件details ontogglealert(1) openopen属性使其默认展开触发ontoggle。video或audio标签的onplay事件结合autoplay属性。body标签的onpageshow事件body onpageshowalert(1)。利用iframe的srcdoc属性iframe srcdoc“scriptalert(1)/script“/iframe。srcdoc属性内的内容会作为一个独立的文档解析可能绕过一些针对当前文档的过滤。5.4 第19关基于CSS的XSS极少见但存在XSS不一定非要执行JavaScript理论上能控制样式表CSS也可能造成危害如窃取数据但难度极高。更常见的是利用CSS表达式IE特有已废弃或style标签的属性。但现代浏览器中纯CSS很难直接执行JS。一种可能的思路是如果输入被放入style标签内或元素的style属性中且过滤不严可以尝试注入expression(...)仅旧版IE或者利用import引入外部资源但这通常无法直接执行脚本。这一关更可能是误导或者考察你是否能想到非常规的向量。在实际测试中应优先关注能直接执行脚本的上下文。5.5 第20关综合审计与代码分析最后一关通常不会引入新的技术而是模拟一个小型、完整的应用程序片段其中包含多个潜在的输入点和复杂的客户端逻辑。你需要像真正的代码审计一样静态分析仔细阅读前端JavaScript代码追踪所有用户可控数据的来源location.*,document.*,window.name,postMessage等。动态调试使用浏览器开发者工具的“源代码”面板在可能的危险函数eval,setTimeout,setInterval,Function constructor,innerHTML,outerHTML,document.write,location.assign,location.replace等上设置断点。数据流追踪观察用户输入是如何经过各种字符串处理函数replace,substring,decodeURIComponent,escape等的过滤规则是否存在逻辑漏洞如只过滤一次、大小写问题、顺序问题。多步骤利用可能需要结合两个或多个输入点或者利用一个点的输出作为另一个点的输入形成链式利用。例如代码可能先从URL参数#data获取值经过一次replace(‘’, ‘’)过滤然后赋值给一个变量。这个变量又被另一个函数使用该函数用innerHTML输出。这里过滤只删除了第一个你可以用script来绕过删除第一个后剩下script。6. 实战后的思考从靶场到真实世界通关XSS-labs只是第一步它帮你建立了对XSS漏洞原理和基础绕过手法的直觉。但在真实的渗透测试或代码审计中情况要复杂得多。6.1 自动化探测与手动验证在实战中我们通常会先用自动化工具如Burp Suite的Active Scan 或专门的XSS扫描器进行初步的爬取和测试。这些工具能快速发现明显的、标准化的漏洞。但是高价值的、需要绕过的XSS漏洞几乎100%依赖于手动测试。自动化工具生成的Payload往往很直接遇到过滤就容易失败。你需要分析过滤逻辑通过输入test“’等测试串观察返回结果判断是黑名单过滤、编码、还是删除。Fuzzing测试使用Burp Intruder或自定义脚本对关键参数进行模糊测试尝试大量特殊的字符和Payload变体观察哪些被拦截哪些能通过。上下文切换同一个参数在GET、POST、Cookie、Header中服务端的处理逻辑可能不同。多尝试几种请求方式。6.2 绕过WAFWeb应用防火墙的常见思路企业级应用通常部署了WAF它们有更强大的规则集。绕过WAF需要更多的技巧和耐心混淆与编码HTML实体编码变成lt;但注意解码时机。URL编码变成%3C变成%3E。有时双重编码%253C可能有效。Unicode编码变成\u003c在JavaScript字符串中。Base64编码结合data:协议使用如img src“data:image/svgxml;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoMSk“这是svg onloadalert(1)的base64编码。拆分与拼接利用JavaScript的字符串拼接能力。如scripteval(‘al’’ert(1)‘)/script。利用冷门标签与事件WAF规则可能覆盖不全。尝试svg,math,details,audio等标签以及onbegin,onend,oncanplay等不常见事件。协议与伪协议除了javascript:旧版浏览器还支持vbscript:data:协议也非常强大。请求方式变换将GET请求改为POST或者将参数放在JSON body中有时能绕过基于URL和参数名的检测规则。6.3 防御视角如何写出安全的代码作为开发者理解攻击手法是为了更好地防御。根本的防御原则是“一切用户输入皆不可信”。输入验证在服务端对输入进行严格的、白名单式的格式验证例如邮箱地址就只允许特定字符。但验证不能替代输出编码。输出编码这是防御XSS最有效的手段。根据数据输出的上下文进行正确的编码输出到HTML正文使用HTML实体编码 - amp;, - lt;, - gt;,” - quot;,’ - #x27;。输出到HTML属性同上并且属性值一定要用引号括起来。输出到JavaScript使用JavaScript Unicode编码\uXXXX。输出到URL使用URL编码。现代前端框架如React, Vue, Angular等默认提供了安全的输出机制如React的JSX会自动转义但也要警惕使用dangerouslySetInnerHTML或v-html等危险API。使用CSP内容安全策略是一个强大的深度防御措施。通过HTTP头Content-Security-Policy你可以告诉浏览器只允许加载来自特定源的脚本、样式、图片等。即使攻击者注入了脚本如果源不在白名单内浏览器也不会执行。例如Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com;。设置HttpOnly Cookie为会话Cookie设置HttpOnly标志可以阻止JavaScript通过document.cookie访问从而缓解Cookie窃取攻击。避免危险函数在代码审计中警惕直接使用innerHTML,outerHTML,document.write(),eval(),setTimeout()/setInterval()第一个参数为字符串时new Function()等。如果必须使用必须对输入进行严格的净化和编码。