尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

XSS攻击实战:从原理到绕过,掌握Payload构造的核心思维

XSS攻击实战:从原理到绕过,掌握Payload构造的核心思维 1. 项目概述从“背代码”到“写代码”的思维跃迁如果你在学Web安全尤其是跨站脚本攻击XSS时还在对着网上搜来的那一长串“Payload大全”死记硬背那我得给你泼盆冷水你正在走一条效率最低、最容易被淘汰的老路。我见过太多新手面对XSS-labs这类经典靶场第一反应就是去搜“通关Payload”然后像抄作业一样一个个填进去关卡是过了但关上浏览器脑子里除了几个alert(1)的变形什么都没剩下。这就像只背熟了数学公式却完全不懂其推导过程和适用场景题目稍一变形立刻束手无策。这个项目就是要彻底扭转这种局面。我们不谈那些空洞的“理解原理”而是直接上手以XSS-labs靶场为沙盘带你亲历一场“Payload外科手术”。我们的目标不是让你记住一百种绕过方法而是让你掌握一种核心能力面对任何过滤与防护都能像搭积木一样从底层逻辑出发现场构造出有效的攻击代码。你会发现那些看似神奇的绕过技巧背后无非是HTML解析、JavaScript执行以及浏览器容错性这几个基本规则的组合运用。当你理解了为什么scrscriptipt能绕过为什么onmouseover和onclick效果不同你就不再是Payload的搬运工而是它的创造者。接下来我会带你拆解XSS攻击链路上的每一个环节从最基本的标签构造到应对现代WAF的奇技淫巧最后回归到XSS-labs靶场的实战通关。我们不止步于“怎么做”更要深挖“为什么能这么做”。准备好了吗让我们开始这场从“记忆”到“创造”的思维升级。2. 核心思路拆解理解浏览器的“宽容”与过滤器的“迟钝”在开始写第一行绕过代码之前我们必须建立两个核心认知浏览器是如何理解我们输入的代码的以及防护机制靶场或WAF通常是如何拦截我们的。这两者之间的“理解偏差”就是我们构造Payload的生存空间。2.1 浏览器解析HTML的三层逻辑浏览器渲染页面的过程可以粗略分为三层解析、构建、执行。我们的Payload主要在前两层做文章。词法分析Tokenization浏览器读取HTML源代码将其拆分成一个个“令牌”Tokens比如开始标签div、属性onclick、属性值alert(1)、结束标签/div。这个过程相对“宽容”它会自动纠正许多不规范的写法。DOM树构建浏览器根据令牌之间的关系构建出文档对象模型DOM树。这里有一个关键点浏览器在将文本转换为DOM节点时会进行HTML实体解码。这意味着如果你输入lt;scriptgt;在DOM树中它会被还原成script。JavaScript引擎执行当解析到script标签或事件被触发时浏览器内的JavaScript引擎如V8会执行其中的代码。引擎同样有自己的解析规则比如对Unicode转义字符\u0061的解码。一个至关重要的原则过滤器的检查往往发生在服务器端在响应到达浏览器之前。而浏览器的解析发生在客户端在收到响应之后。我们的Payload就是要构造一种“形态”让它能骗过过滤器的简单规则同时又能被浏览器的解析器正确理解。2.2 常见过滤与防护手段的“命门”靶场或初级WAF的过滤方式通常是“黑名单”式的它们有一些共同的、可被利用的弱点字符串匹配直接查找并删除或转义script、onclick、javascript:等敏感字符串。这是最原始也最好绕过的方式。正则表达式过滤使用正则表达式匹配比如/script.*?.*?\/script/gi来匹配整个脚本块。但正则表达式是“死”的浏览器解析是“活”的。标签/属性白名单只允许特定的、安全的标签和属性如b,i,class,id。这需要更精细的利用方式。HTML编码输出将用户输入中的特殊字符如,,,,转换为HTML实体如lt;,gt;,amp;。这需要结合上下文判断是否可被二次解码。我们的绕过策略就是针对这些“命门”设计的。下面我们进入实战环节从最简单的场景开始一步步搭建你的Payload武器库。3. 基础绕过手法利用解析差异构造有效载荷让我们暂时忘掉那些复杂的组合技先从最本质的、浏览器与过滤器“认知不同”的几个点开始。3.1 标签闭合与属性构造的“花招”当过滤器简单地删除script字符串时我们可以利用浏览器的“纠错”能力。案例1嵌套绕过这是最经典的入门技巧。假设过滤器发现script就删除一次。Payload:scrscriptiptalert(1)/scrscriptipt过滤器视角它看到字符串找到中间的script并将其删除结果变成了scriptalert(1)/script。正中下怀你的思考这利用了过滤器“一次匹配、一次处理”的简单逻辑。如果过滤器是递归删除所有匹配项呢你可以尝试scrscrscriptiptiptalert(1)/scrscrscriptiptipt或者换用其他方式。案例2利用标签属性与空格过滤可能不允许onclick但浏览器对属性格式很宽容。Payload:div onclick alert(xss)(在onclick和之间插入空格)Payload:div onclickalert(xss)(省略属性值的引号)Payload:div ONCLICKalert(xss)(使用大小写)你的思考过滤器的正则可能是/onclick\s*/i但onclick 可能就不匹配了。省略引号在HTML中是有效的只要属性值不包含空格。大小写混合是为了绕过简单的strtolower()后匹配。注意属性值省略引号虽然方便但如果值内包含空格浏览器会认为属性提前结束了。在实战中优先使用引号包裹是更稳妥的做法。3.2 事件处理器的广阔天地很多人只知道onclick和onerror。实际上HTML规范中定义了数十种事件。当常见事件被过滤时那些生僻事件就是我们的突破口。常见事件黑名单往往不全onmouseenter,onmouseleave鼠标进入/离开元素时触发。onfocus,onblur元素获得/失去焦点时触发需要配合autofocus属性。onload资源加载完成适用于img,iframe,svg等。onpageshow,onbeforeunload页面生命周期事件。实战技巧你可以系统性地测试所有on*事件。一个快速的方法是在浏览器的开发者工具控制台中输入Object.getOwnPropertyNames(HTMLElement.prototype).filter(name name.startsWith(on))来查看当前浏览器支持的事件列表。3.3 JavaScript伪协议与编码把戏a hrefjavascript:alert(1)这种形式很常见javascript:这个协议标识符也常被过滤。绕过方法利用浏览器的URL解码Payload:a hrefjava#115;cript:alert(xss)link/a原理#115;是s的HTML十进制实体。过滤器可能直接匹配字符串javascript:但匹配不到java#115;cript:。然而浏览器在解析href属性值时会先进行HTML实体解码将#115;还原为s最终得到可执行的javascript:协议。变种还可以使用十六进制实体#x73;或者混合编码j#x61;vascript:。一个更隐蔽的角落VBScript在旧版本IE或IE兼容模式下vbscript:协议同样可以执行代码。Payload:a hrefvbscript:MsgBox(XSS)link/a你的思考这提醒我们绕过时思维不能局限于JavaScript。目标系统的浏览器环境特别是老旧内网系统可能支持一些已被现代标准废弃的特性。4. 高级上下文攻击精准打击HTML结构中的弱点基础绕过是“力大砖飞”而高级技巧则要求你像狙击手一样仔细观察输入点所处的上下文然后进行精准打击。这是XSS-labs中后期关卡的核心考点。4.1 属性值上下文中的突破假设你的输入出现在一个标签的属性值里比如input typetext value【你的输入】过滤器很可能转义了和让你无法插入新标签。怎么办我们的目标是提前闭合这个value属性然后添加我们自己的事件处理器。第一步闭合引号。输入闭合原有的value属性。第二步添加事件。在闭合引号后直接写事件处理器例如onmouseoveralert(1)。第三步处理后续字符。为了不影响HTML结构通常用//注释掉后面原生的或者用一个空格和另一个假属性来“消化”它们。最终Payload: onmouseoveralert(1) x生成的HTML:input typetext value onmouseoveralert(1) x原理x是一个自定义属性它吃掉了原生的闭合引号和尖括号保证了HTML结构依然合法。如果引号被转义了呢如果输出是value\你的输入\双引号被转义成了quot;或\上述方法可能失效。这时可以尝试不闭合引号而是利用事件处理器本身可以不用引号的特性但值不能有空格并确保后面有空格或来结束属性。尝试Payload:onmouseoveralert(1) //生成的HTML:input typetext value onmouseoveralert(1) //结果value属性的值变成了一个很长的字符串onmouseoveralert(1) //这通常不会触发XSS因为onmouseover成了value的一部分而不是属性。所以这种情况下需要另寻他法比如寻找其他未过滤的事件或利用JavaScript字符串拼接。4.2 脚本标签内部的代码注入这是另一种常见且危险的场景。你的输入被直接拼接到JavaScript代码中script var userInput 【你的输入】; // ... 后续可能用到了 userInput /script过滤器可能转义了引号将变成\。我们的目标是跳出字符串上下文直接执行代码。策略闭合字符串注释掉后续代码。Payload:; alert(1); //生成的代码:var userInput ; alert(1); //;原理第一个闭合了前面的字符串;结束当前语句alert(1);是我们注入的代码//将原代码中剩余的;注释掉防止语法错误。如果分号;被过滤了JavaScript有自动分号插入ASI机制在某些情况下换行也可以表示语句结束。你可以尝试Payload: alert(1)//或者利用运算符-alert(1)-。当代码是var x 【输入】时注入-alert(1)-会变成var x -alert(1)-。这是一个合法的JavaScript表达式空字符串减去alert(1)的执行结果undefined再减去空字符串。虽然可能得到NaN但alert(1)已经被执行了。4.3 利用CSS和HTML样式表达式的“古法”在IE7及更早版本中CSS的expression()属性可以执行JavaScript。虽然如今已基本绝迹但在一些极端老旧的内网系统或特定兼容模式下它仍是一把利器。Payload:div stylecolor: expression(alert(XSS))现代绕过思路即使expression被过滤可以尝试利用CSS解析器的特性。例如IE浏览器会忽略CSS中某些错误并继续解析。可以尝试插入一个无效声明来分割div stylecolor: ; color: expression(alert(XSS))这里color: ;是一个无效的CSS声明但IE可能会忽略它继续解析后面的color: expression(...)。实操心得上下文攻击是XSS的灵魂。每次测试前一定要用浏览器的“查看页面源代码”不是检查元素功能看清楚你的输入被原样放置在了HTML的哪个位置。是在标签内、属性里、JavaScript字符串中还是注释里位置决定了你的攻击策略。5. 编码与混淆对抗深度过滤的“迷彩服”当简单的字符串替换和正则过滤无法绕过时我们就需要祭出编码和混淆技术给Payload穿上“迷彩服”让过滤器“看不见”但浏览器依然“认得”。5.1 多层次编码的艺术编码的核心思想是在传输和存储时变形在执行时还原。HTML实体编码用于绕过对HTML标签和属性的检查。-lt;或#60;或#x3c;-gt;-amp;-quot;Payload示例lt;img srcx onerroralert(1)gt;何时有效当你的输入被直接输出到HTML正文中且浏览器会自动对其进行解码时。如果输出点在script标签内的字符串里HTML实体编码会被当作普通文本无法解码成标签。JavaScript Unicode转义用于绕过对JavaScript关键字的检查。alert-\u0061\u006c\u0065\u0072\u0074Payload示例script\u0061\u006c\u0065\u0072\u0074(1)/script原理JavaScript引擎在解析字符串或标识符时会识别\uXXXX形式的Unicode转义序列并将其转换回对应的字符。这对于绕过基于关键词黑名单的过滤器非常有效。URL编码常用于javascript:伪协议或data:协议中。javascript:alert(1)-javascript%3Aalert%281%29Payload示例a hrefjavascript%3Aalert%281%29click/a注意URL编码通常需要结合场景。在HTML属性中浏览器会自动解码但在某些通过JavaScript动态赋值href的场景可能需要手动解码。混合编码与嵌套编码这是高级技巧。例如先对script进行Unicode转义再将整个字符串进行HTML实体编码。构思最终想让浏览器执行scriptalert(1)/script。步骤1将script转义为\u0073\u0063\u0072\u0069\u0070\u0074。步骤2组合成\u0073\u0063\u0072\u0069\u0070\u0074alert(1)/\u0073\u0063\u0072\u0069\u0070\u0074。步骤3将整个字符串中的和进行HTML实体编码。最终Payloadlt;\u0073\u0063\u0072\u0069\u0070\u0074gt;alert(1)lt;/\u0073\u0063\u0072\u0069\u0070\u0074gt;浏览器解析顺序先HTML解码得到\u0073...然后JavaScript引擎在解析script标签内容时再将\u0073等解码为s最终识别为有效的script标签。5.2 非常规标签与属性挖掘当script、img、svg等常见标签被全面封杀时我们需要挖掘HTML和浏览器API中的“冷兵器”。details标签的ontoggle事件details ontogglealert(1) open用户点击展开/折叠时触发。open属性使其默认展开可能立即触发。audio/video标签的oncanplay事件video srcx oncanplayalert(1)/video当媒体文件可以开始播放时触发。即使src无效事件也可能被触发。marquee标签的onstart事件marquee onstartalert(1)test/marquee这个古老的跑马灯标签其事件常被忽略。body标签的onhashchange事件body onhashchangealert(1)当URL的哈希部分发生变化时触发。结合iframe或自身导航可以触发。form与button的formaction属性formbutton formactionjavascript:alert(1)提交/button/formformaction属性可以覆盖form的action点击按钮会触发JavaScript。工具推荐手动挖掘太累可以借助DOM XSS Scanner这类浏览器扩展或XSStrike、xssor2等工具它们内置了大量不常见的标签和事件Payload可以帮你快速枚举测试。5.3 利用浏览器Bug与解析特性这类方法依赖于特定浏览器或特定版本的解析器缺陷通用性不强但有时有奇效。空字节截断在PHP旧版本中%00空字节可能被系统函数错误处理。Payloadscri%00ptalert(1)/scri%00pt原理某些服务端过滤逻辑在C语言层面处理字符串时遇到%00可能认为字符串结束从而忽略了后面的检查。但浏览器在解析时%00可能被忽略或当作普通字符最终仍能识别script。字符集编码漏洞如果服务器返回的HTTP头或HTML meta标签中字符集设置不当且用户输入被错误解析可能导致XSS。经典案例UTF-7。如果页面以UTF-7编码且未正确声明那么ADw-scriptAD4-alert(1)ADw-/scriptAD4-会被解析为scriptalert(1)/script。但现在绝大多数网站都会显式声明为UTF-8此漏洞已很少见。HTML5解析器差异不同浏览器对畸形HTML的容错处理不同。Payloadimg/srcx/onerroralert(1)说明在img和src之间没有空格。某些解析器如旧版IE可能无法正确识别属性导致/onerror被当作src值的一部分。但现代浏览器大多能容错仍将其解析为srcx和onerroralert(1)两个属性。这更多是用于绕过一些简单的基于空格分割的过滤器。重要警告依赖浏览器Bug的Payload极不稳定可能只在特定浏览器、特定版本下生效。在实战中它们应作为最后的手段并且必须经过在目标环境下的严格测试。在XSS-labs这类靶场中它们可能是通关特定关卡的钥匙但在真实渗透测试中优先使用标准、通用的方法。6. 实战XSS-labs关卡思路分析与Payload构造理论说得再多不如实战一遍。我们以XSS-labs靶场一个常见的、分关卡递增难度的XSS练习平台为例挑选几个有代表性的关卡演示如何应用上述思维而不是背诵Payload。假设关卡1无任何过滤目标弹出对话框。你的思路这是热身关卡直接测试最基本的脚本标签。构造过程输入scriptalert(document.domain)/script。成功。思考延伸为什么用document.domain而不是简单的数字因为它能证明你执行了JavaScript并成功获取了当前页面的域名信息这在实战中更有价值。假设关卡5过滤了script标签目标绕过对script关键词的过滤。你的思路过滤器可能直接删除script和/script字符串。构造过程尝试嵌套scrscriptiptalert(1)/scrscriptipt。如果不行尝试大小写ScRiPtalert(1)/ScRiPt。如果还不行放弃script标签转向其他带事件的标签img srcx onerroralert(1)。核心不要吊死在一棵树上。标签有很多事件也有很多。假设关卡10输入出现在input标签的value属性中且被转义目标利用属性上下文。页面代码可能为input typetext value【你的输入】 readonly你的思路无法插入新标签需要闭合value属性然后添加新的事件属性。构造过程输入 onclickalert(1)。生成的HTML为input typetext value onclickalert(1) readonly。但readonly属性可能会阻止点击。尝试获取焦点事件 onfocusalert(1) autofocus。这里我们添加了autofocus属性让元素自动获得焦点从而触发onfocus。注意我们用autofocus来“吃掉”原生的保持HTML结构正确。最终Payload可能需要根据页面具体结构微调比如后面是还是 /。假设关卡15输入被放入JavaScript字符串变量中引号被转义目标跳出字符串执行代码。页面代码可能为scriptvar a 【你的输入】; /script你的思路闭合字符串注释掉后续代码。构造过程输入; alert(1);//。生成var a ; alert(1);//;。完美。如果分号被过滤尝试-alert(1)-。生成var a -alert(1)-;。这是一个合法的JS表达式会执行alert。如果反斜杠转义了单引号\使得我们无法闭合字符串则需要寻找其他注入点或者尝试Unicode转义等其他方式。假设关卡20综合过滤使用了类似WAF的规则现象script、on*、javascript:、、引号等都被过滤或转义。你的思路需要多角度尝试和组合技巧。探测过滤规则先提交无害payload如b、i看标签是否被保留。提交scri pt中间有空格看是删除script还是删除整个标签。尝试编码提交HTML实体编码的payload如lt;img srcx onerroralert(1)gt;看是否被解码。尝试冷门标签和事件如svgscriptalert(1)/script/svg或body onhashchangealert(1)。尝试data:协议object datadata:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg这是scriptalert(1)/script的base64编码。利用DOM型XSS如果反射型不行查看页面源代码是否有像eval(location.hash.substring(1))这样的代码尝试通过URL片段#后的部分注入。7. 防御视角与Payload编写原则真正精通绕过的人也必然深刻理解防御。从防御者角度思考能让你写出更刁钻的Payload。7.1 现代WAF的常见策略与我们的对策语义分析不仅匹配字符串还尝试理解代码结构。例如识别script标签即使被分割也可能重组。对策转向使用非脚本标签的事件处理器如img onerror或利用HTML本身的动态特性如iframe srcdoc。规范化与解码WAF会模拟浏览器对输入进行多层解码HTML实体、URL、Unicode后再检查。对策使用非常规的、有歧义的编码方式或利用解码顺序的差异。例如混合使用十进制和十六进制实体或在JavaScript字符串中使用八进制转义\141代表a。上下文感知区分输入是出现在HTML、属性、JavaScript还是CSS上下文中应用不同的过滤规则。对策这正是我们前面强调的。精确判断上下文选择最适合该上下文的攻击向量。如果在CSS中尝试expression()或url(javascript:...)如果在JS字符串中想方设法跳出字符串。7.2 编写健壮Payload的黄金法则根据以上分析我总结出几条编写Payload的实用原则先探测后攻击永远不要一上来就扔最复杂的Payload。先用b、“、‘、、等试探过滤规则和编码情况。观察它们是被删除、转义还是原样输出。优先使用标准特性依赖于浏览器标准支持且广泛兼容的标签和事件如img onerror、svg onload其稳定性远高于利用浏览器Bug的奇技淫巧。保持简洁在满足绕过条件的前提下Payload越短越好。短Payload不仅隐蔽而且受长度限制的影响小也更容易通过某些输入框的长度限制。考虑用户体验用于隐蔽攻击在真实的渗透测试中你或许不希望弹出一个明显的alert框。可以考虑使用fetch()或XMLHttpRequest将Cookie悄无声息地发送到你的服务器或者进行页面重定向、键盘记录等。在靶场中alert或confirm是证明执行成功的标志。多准备几个备选方案一个Payload失败了立刻换下一个思路。你的武器库应该包含标签事件类、JavaScript伪协议类、CSS表达式类针对老系统、编码混淆类、以及针对特定上下文属性内、脚本内的专用Payload。8. 工具辅助与思维升华最后我们来谈谈工具。工具不是用来替代思考的而是用来提升效率、拓展思路的。Burp Suite Intruder / Turbo Intruder用于自动化Fuzzing。你可以将Payload中的某个位置如标签名、事件名设为变量加载一个庞大的字典包含各种标签、事件、编码变体进行批量测试观察哪些没有被过滤。XSStrike一个先进的XSS检测工具。它不仅能自动检测还能基于上下文智能生成Payload并且有“模糊测试”模式可以自动探测过滤规则。浏览器开发者工具这是你最好的朋友。用“查看源代码”确认输入点。用控制台Console测试JavaScript代码片段。用调试器Debugger跟踪代码执行流程。用网络Network面板观察是否有Payload被拦截。然而比所有工具都重要的是思维模式的转变。看完这篇文章我希望你能养成这样的习惯看到输入框不再是“该填什么Payload”而是“这个输入会落到页面的哪个上下文”看到过滤提示不再是“这个规则怎么绕”而是“这个规则是黑名单还是白名单它处理字符串的粒度有多细”构造Payload时不再是“从列表里抄一个”而是“基于当前上下文和过滤规则我如何组合标签、事件、编码来达成目的”XSS-labs通关不是终点它只是一个训练场。真正的战场是千变万化的真实Web应用。掌握了“拆解-分析-构造”这套方法论你就能以不变应万变从被动记忆的“学生”成长为主动创造的“专家”。记住最强的Payload永远是你基于对目标系统深刻理解而现场编写的那一个。
返回列表