CVE-2016-1000031ApacheCommonsFileUpload反序列化漏
洞深⼊分析
漏洞原因
先来看DiskFileItem.java 代码
/**
* Reads the state of this object during deserialization.
*
* @param in The stream from which the state should be read.
*
* @throws IOException if an error occurs.
* @throws ClassNotFoundException if class cannot be found.
*/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
// read values
in.defaultReadObject();
/* One expected use of serialization is to migrate HTTP sessions
* containing a DiskFileItem between JVMs. Particularly if the JVMs are
* on different machines It is possible that the repository location is
* not valid so validate it.
*/
if (repository != null) {
if (repository.isDirectory()) {
// Check path for nulls
if (Path().contains("\0")) {
throw new IOException(format(
"The repository [%s] contains a null character",
}
} else {
throw new IOException(format(
"The repository [%s] is not a directory",
}
}
OutputStream output = getOutputStream();
if (cachedContent != null) {
output.write(cachedContent);
} else {
FileInputStream input = new FileInputStream(dfosFile);
dfosFile.delete();
dfosFile = null;
}
output.close();
cachedContent = null;
}
DiskFileItem⾥⾯封装了反序列的⽅法,在反序列化的⽅法⾥
1. 序列化中的数据可以保存在服务器的临时⽂件中
2. 复制服务器的⽂件到临时⽂件
3. 删除服务器的⽂件
private transient DeferredFileOutputStream dfos;
public OutputStream getOutputStream()
throws IOException {
if (dfos == null) {
File outputFile = getTempFile();
dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
}
return dfos;
}
dfos 是不可序列化的,反序列化中dfos为空,只能获取临时⽂件
protected File getTempFile() {
if (tempFile == null) {
File tempDir = repository;
if (tempDir == null) {
tempDir = new Property("pdir"));
}
String tempFileName = format("upload_%s_%s.tmp", UID, getUniqueId());
tempFile = new File(tempDir, tempFileName);
}
return tempFile;
}
也就是基于设置的repository ⽬录的临时⽂件
从风险⾓度来考虑
1. 复制到临时⽂件upload_p
2. 可以删除任意有删除权限的⽂件,问题的风险性并不⾼
CVE的描述
CVE所提⽰的风险感觉定义不清楚
Apache Commons FileUpload DiskFileItem File Manipulation Remote Code Execution
DiskFileItem中注⼊执⾏代码,也⽆法在反序列化的时候被执⾏,这个和collection的反序列化的漏洞的风险是完全不同的,不清楚为何描述成可以操纵执⾏远端代码,是否是只要是反序列化的漏洞就是可以执⾏远端代码?
JDK6下的风险
JDK7以上在Java的file相关的基础类中都做了空字符的保护,这也是在针对java的string 和 c char的结束⽅式不⼀致,在Java中⽂件的操作中使⽤String这种char 数组,⽽C中的char 是以空字符为结束符,所以java操作的⽂件中很容易通过注⼊空字符来操作完全不同的⽂件⽐如Java File file = new File("/\0.jsp") 看起来再操作 \0.jsp 实际上在底层调⽤的(本质还是c读写⽂件)是在操作
在JDK7以后的版本File ⾥⾯会有⼀个判断是否有空字符的函数
final boolean isInvalid() {
if (status == null) {
status = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED
: PathStatus.INVALID;
}
return status == PathStatus.INVALID;
}
⽽在很多关键操作中都会有isInValid 函数的判断,避免被注⼊空字符绕过风险,但是JDK6并没有进⾏防护,在JDK6是可以通过输⼊空字符串进⾏⽂件名控制。
1. 我们先看⽬录的空字符是否有机会注⼊,在readObject的代码中,我们看到了在判断⽬录的时候,对路径进⾏了空字符保护
if (repository != null) {
if (repository.isDirectory()) {
// Check path for nulls
if (Path().contains("\0")) {
throw new IOException(format(
"The repository [%s] contains a null character",
cve漏洞库
}
} else {
throw new IOException(format(
"The repository [%s] is not a directory",
}
}
2. 在⽂件名中我们看是否有机会注⼊空字符
String tempFileName = format("upload_%s_%s.tmp", UID, getUniqueId());
UID, getUniqueId 都是可以构造的,但只能构造upload_为前缀的⽂件名
3. 通过相对路径构建任意⽂件名
构建 设置UID = /../../anyfile\0
只要有upload_为前缀的⽬录存在,通过相对路径,可以⽣成任意⽂件,同时最后注⼊空字符,结束⽂件名。这样就可以在服务器端⽣产任意⽂件
⽣成任意⽂件前提
1. JDK6 以下的版本
2. 必须在系统⾥存在upload_为前缀的⽬录存在
显然形成这样的攻击,前提条件是苛刻的
其他的风险
Dos攻击
因为使⽤了 DeferredFileOutputStream ,可以设置⼀个超⼤的 sizeThreshold(保存在内存⾥),设置⼀个系统⾥的超⼤⽂件 dfosFile在反序列化时复制dfosFile 到DefferredFileOutputStream导致JVM OOM
删除任意可删除的⽂件
设置dfosFile 为任意⽂件,因为反序列化后,复制完后会删除dfosFile,这样达到删除任意⽂件的⽬的
通过上述分析,我们可以看到上传⽂件反序列化的问题,并没有什么⾼危操作,⽽且有些攻击还必须有场景配合。
APACHE的官⽅态度
Apache ⽬前给出⼏个⽅案
1. 不认为是问题
2. 去除DiskFileItem.java的反序列化功能,这显然遭到了反对
3. 和collection解决⽅案⼀样在添加个系统参数的开关,如果打开的话,才能进⾏反序列化,就好像如果
你打开了开关,就必须⾃⼰校验当从外部⾮可信域传⼊的时候
⽬前还⽆选择结果
反序列化JVM的不作为
⽬前JVM在反序列化的时候,JVM⽆法通过沙箱设置反序列化的类的⿊⽩名单,只确保的反序列化类是在class loader 能初始化的。
对反序列化的保护,⽬前常见的解决⽅案扩展 ObjectInputStream,在resolveClass⽅法⾥进⾏类的⿊⽩名单保护,但涉及到上层代码的改动。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。