[ 
https://issues.apache.org/jira/browse/FILEUPLOAD-279?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Michiel Weggen updated FILEUPLOAD-279:
--------------------------------------
    Description: 
http://www.tenable.com/security/research/tra-2016-12

Summary

There exists a Java Object in the Apache Commons FileUpload library that can be 
manipulated in such a way that when it is deserialized, it can write or copy 
files to disk in arbitrary locations. Furthermore, while the Object can be used 
alone, this new vector can be integrated with ysoserial to upload and execute 
binaries in a single deserialization call. This may or may not work depending 
on an application's implementation of the FileUpload library.

Background

In late 2015 FoxGlove Security released a write up on using Chris Frohoff’s 
yososerial tool to gain remote code execution on a variety of commercial 
products, based on a presentation at AppSec Cali in January, 2015. The 
ysoserial tool uses “gadgets” in Apache Commons Collections, Groovy, and Spring 
that do “unexpected” things during deserialization. Specifically, the ysoserial 
payloads eventually execute Runtime.getRuntime().exec() allowing for remote 
Java code execution.

The Apache Commons project maintains a library called “FileUpload” to make “it 
easy to add robust, high-performance, file upload capability to your servlets 
and web applications.” One of the classes in the FileUpload library is called 
“DiskFileItem”. A DiskFileItem is used to handle file uploads. Interestingly, 
DiskFileItem is serializable and implements custom writeObject() and 
readObject() functions.

DiskFileItem’s readObject Implementation

Here is the implementation that currently exists at the projects repository tip 
(as of 1/28/16):


632    private void readObject(ObjectInputStream in)
633            throws IOException, ClassNotFoundException {
634        // read values
635        in.defaultReadObject();
636
637        /* One expected use of serialization is to migrate HTTP sessions
638         * containing a DiskFileItem between JVMs. Particularly if the JVMs 
are
639         * on different machines It is possible that the repository location 
is
640         * not valid so validate it.
641         */
642        if (repository != null) {
643            if (repository.isDirectory()) {
644                // Check path for nulls
645                if (repository.getPath().contains("\0")) {
646                    throw new IOException(format(
647                            "The repository [%s] contains a null character",
648                            repository.getPath()));
649                }
650            } else {
651                throw new IOException(format(
652                        "The repository [%s] is not a directory",
653                        repository.getAbsolutePath()));
654            }
655        }
656
657        OutputStream output = getOutputStream();
658        if (cachedContent != null) {
659            output.write(cachedContent);
660        } else {
661            FileInputStream input = new FileInputStream(dfosFile);
662            IOUtils.copy(input, output);
663            IOUtils.closeQuietly(input);
664            dfosFile.delete();
665            dfosFile = null;
666        }
667        output.close();
668
669        cachedContent = null;
670    }
This is interesting due to the apparent creation of files. However, after 
analyzing the state of DiskFileItem after serialization it became clear that 
arbitrary file creation was not supposed to be intended:

dfos (a type of OutputStream) is transient and therefore it is not serialized. 
dfos is regenerated by the getOutputStream() call above (which also generates 
the new File to write out to).
The “repository” (or directory that the file is written to) has to be valid at 
the time of serialization in order for successful deserialization to occur.
If there is no “cachedContent” then readObject() tries to read in the file from 
disk.
That filename is always generated via getOutputStream.
Serialized Object Modification

The rules listed above do not take into account that someone might modify the 
serialized data before it is deserialized. Three important elements get 
serialized that we can modify:

The repository path (aka the directory that the file is read/written from).
If there is cachedContent (i.e. data that didn’t get written to the file) then 
that gets serialized
If there is no cachedContent (i.e. all data was written to disk) the full path 
to the output file exists.
The threshold value that controls if “cachedContent” is written to disk or not.
Modifying these three elements in the serialized object gives us the ability to:

Create files wherever we have permission on the system. The caveat here is that 
we only have control of the file path and not the final filename.
Copy the contents of files from one file on the system to a location we specify 
(again we only control the directory path and not the filename). This will also 
attempt to delete the file we copy from.. so be careful.

  was:http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-1000031


> CVE-2016-1000031 - Apache Commons FileUpload DiskFileItem File Manipulation 
> Remote Code Execution
> -------------------------------------------------------------------------------------------------
>
>                 Key: FILEUPLOAD-279
>                 URL: https://issues.apache.org/jira/browse/FILEUPLOAD-279
>             Project: Commons FileUpload
>          Issue Type: Bug
>    Affects Versions: 1.3.2
>            Reporter: Michiel Weggen
>            Priority: Critical
>              Labels: security
>
> http://www.tenable.com/security/research/tra-2016-12
> Summary
> There exists a Java Object in the Apache Commons FileUpload library that can 
> be manipulated in such a way that when it is deserialized, it can write or 
> copy files to disk in arbitrary locations. Furthermore, while the Object can 
> be used alone, this new vector can be integrated with ysoserial to upload and 
> execute binaries in a single deserialization call. This may or may not work 
> depending on an application's implementation of the FileUpload library.
> Background
> In late 2015 FoxGlove Security released a write up on using Chris Frohoff’s 
> yososerial tool to gain remote code execution on a variety of commercial 
> products, based on a presentation at AppSec Cali in January, 2015. The 
> ysoserial tool uses “gadgets” in Apache Commons Collections, Groovy, and 
> Spring that do “unexpected” things during deserialization. Specifically, the 
> ysoserial payloads eventually execute Runtime.getRuntime().exec() allowing 
> for remote Java code execution.
> The Apache Commons project maintains a library called “FileUpload” to make 
> “it easy to add robust, high-performance, file upload capability to your 
> servlets and web applications.” One of the classes in the FileUpload library 
> is called “DiskFileItem”. A DiskFileItem is used to handle file uploads. 
> Interestingly, DiskFileItem is serializable and implements custom 
> writeObject() and readObject() functions.
> DiskFileItem’s readObject Implementation
> Here is the implementation that currently exists at the projects repository 
> tip (as of 1/28/16):
> 632    private void readObject(ObjectInputStream in)
> 633            throws IOException, ClassNotFoundException {
> 634        // read values
> 635        in.defaultReadObject();
> 636
> 637        /* One expected use of serialization is to migrate HTTP sessions
> 638         * containing a DiskFileItem between JVMs. Particularly if the 
> JVMs are
> 639         * on different machines It is possible that the repository 
> location is
> 640         * not valid so validate it.
> 641         */
> 642        if (repository != null) {
> 643            if (repository.isDirectory()) {
> 644                // Check path for nulls
> 645                if (repository.getPath().contains("\0")) {
> 646                    throw new IOException(format(
> 647                            "The repository [%s] contains a null 
> character",
> 648                            repository.getPath()));
> 649                }
> 650            } else {
> 651                throw new IOException(format(
> 652                        "The repository [%s] is not a directory",
> 653                        repository.getAbsolutePath()));
> 654            }
> 655        }
> 656
> 657        OutputStream output = getOutputStream();
> 658        if (cachedContent != null) {
> 659            output.write(cachedContent);
> 660        } else {
> 661            FileInputStream input = new FileInputStream(dfosFile);
> 662            IOUtils.copy(input, output);
> 663            IOUtils.closeQuietly(input);
> 664            dfosFile.delete();
> 665            dfosFile = null;
> 666        }
> 667        output.close();
> 668
> 669        cachedContent = null;
> 670    }
> This is interesting due to the apparent creation of files. However, after 
> analyzing the state of DiskFileItem after serialization it became clear that 
> arbitrary file creation was not supposed to be intended:
> dfos (a type of OutputStream) is transient and therefore it is not 
> serialized. dfos is regenerated by the getOutputStream() call above (which 
> also generates the new File to write out to).
> The “repository” (or directory that the file is written to) has to be valid 
> at the time of serialization in order for successful deserialization to occur.
> If there is no “cachedContent” then readObject() tries to read in the file 
> from disk.
> That filename is always generated via getOutputStream.
> Serialized Object Modification
> The rules listed above do not take into account that someone might modify the 
> serialized data before it is deserialized. Three important elements get 
> serialized that we can modify:
> The repository path (aka the directory that the file is read/written from).
> If there is cachedContent (i.e. data that didn’t get written to the file) 
> then that gets serialized
> If there is no cachedContent (i.e. all data was written to disk) the full 
> path to the output file exists.
> The threshold value that controls if “cachedContent” is written to disk or 
> not.
> Modifying these three elements in the serialized object gives us the ability 
> to:
> Create files wherever we have permission on the system. The caveat here is 
> that we only have control of the file path and not the final filename.
> Copy the contents of files from one file on the system to a location we 
> specify (again we only control the directory path and not the filename). This 
> will also attempt to delete the file we copy from.. so be careful.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to