前言
FileUpload1
实验环境:jdk1.8.0_211、commons-fileupload:commons-fileupload:1.3.1、commons-io:commons-io:2.4
代码分析
WRITE
首先看官方给的参数提示
1 | * Arguments: |
然后看代码,我们首先分析- write;destDir;ascii-data这套参数,其对应如下图所示的代码
跟入
看到提示,写入数据到xxx.tmp中,跟入makePayload方法
1 | File repository = new File(repoPath); |
上面就是exp中关于- write;destDir;ascii-data参数的全过程
下面看一下触发点,不过在看触发之前,由于该类重写了writeObject,所以我们先看一下该类的writeObject
1 | private void writeObject(ObjectOutputStream out) throws IOException { |
this.dfos.isInMemory() 这个函数跟进入后发现其含义为,如果this.written字段<=this.threshold的话,执行this.cachedContent = this.get(),由于this.threshold在exp中被我们设置为了data.length+1,this.written被设置为了data.length,所以表达式恒成立,结果必然进入到this.cachedContent = this.get()中。
接下来看下this.get()
1 | public byte[] get() { |
接下来看this.dfos.getData()的内容
由于exp中我们已经memoryOutputStream里面写入了东西,所以这里调用到this.memoryOutputStream.toByteArray()。最后层层返回,this.cachedContent里面的内容就是this.memoryOutputStream.toByteArray()也就是我们参数中输入的ascii-data。
看完了writeObject接下来就是readObject的触发点
1 | private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { |
还是一步步看,首先是in.defaultReadObject(),调用默认的readObject,对类的状态进行还原。
然后调用this.getOutputStream(),这里看下其代码
1 | public OutputStream getOutputStream() throws IOException { |
首先对this.dfos进行判断是否为空,这边注意下图中this.dfos属性被transient标志,所以其readObject后永为空。
接下来看File outputFile = this.getTempFile() ,进入getTempFile
this.repository在new DiskFileItem(“test”, “application/octet-stream”, false, “test”, 100000, repository)中被我们设置为了new File(repoPath),repoPath就是exp参数中我们指定的destDir。
然后和tmpFileName也就是upload_x_x.tmp拼接得到一个this.tempFile并返回。
然后调用上面的代码,this.sizeThreadhold在Reflections.setFieldValue(diskFileItem, “sizeThreshold”, 0)被设置为了0。最后把this.dfos返回并复制给output。
然后调用output.write(this.cachedContent)将内容保存到destdir\upload_x_x.tmp中。最后关闭output流,执行readObject结束。下面是DiskFileItem的API手册。
测试类内容
1 | package ysoserial.payloads.myTest; |
为什么最后把DiskFileItem实体类的sizeThreshold字段反射设置为了0?
看上面API手册哦我们知道,在输入的字节>sizeThreshold时,会把文件自动保存到目标中。所以如果sizeThreshold为0,那么只要我们写入一段字符串就会自动保存到目标目录中。
WRITEOLD
接下来分析这个参数- writeOld;destFile;ascii-data
下面的writePre131时writeOld调用的方法,和write方法比,就是在路径后面加了一个00。很显然就是一个java的00截断。其他的都是和之前分析的write方法一样。java的00截断有jdk版本要求,在晚上看有师傅测试jdk1.7中JDK1.7.0_40(7u40)开始加上了对文件名是否存在\00字符的检查,之前是没有的。而jdk1.6是部分版本还是全部版本
COPYANDDELETE
可以看到thresh被设置为了0,data.length=1,所以在writeObject的时候下面的判断为假
得到this.cachedContent=null,this.dfosFile为String copyAndDelte。
接下来看readObject
由于this.cachedContent为空所以进入else。将String copyAndDelte值赋给了String copyTo+”/upload_x_x.tmp”。达到了复制的效果。而this.dfosFile.delete会删除String copyAndDelte指定的文件,也就是源文件。但我实验的时候没有被删除,不清楚是权限问题还是其他什么问题。