icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons icons_061

[R2] Apache Commons FileUpload DiskFileItem File Manipulation Remote Code Execution (LOBSTER)



Lots Of Bad Serialization To Exploit Remotely (LOBSTER)

We forgot to make a logo and register a domain.


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.


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();
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        }
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();
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:

  1. The repository path (aka the directory that the file is read/written from).
  2. If there is cachedContent (i.e. data that didn’t get written to the file) then that gets serialized
  3. If there is no cachedContent (i.e. all data was written to disk) the full path to the output file exists.
  4. 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:

  1. 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.
  2. 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.

Integrating with ysoserial

While the object’s capabilities are interesting on their own, this new vector becomes much more powerful in conjunction with the current ysoserial gadgets. Combining these objects allows an attacker to upload a binary and execute it in “one” serialized object. In order to integrate these techniques, minor modifications to ysoserial are required. These include:

  • Added creation of a DiskFileItem. Importantly, the DiskFileItem has some data written to it, but not enough to exceed the threshold value.
  • Introduced a LinkedList<Object> and store the DiskFileItem and the ysoserial payload (in that order) in the list.
  • Change CommonCollections1 to call /bin/bash, but this is by no means a requirement.

After ysoserial generates the payload that contains both a DiskFileItem and the selected gadget, the user must edit the DiskFileItem to generate a file upon deserialization. This can be automated by creating a script (e.g. ysoserial_upload_execute.py) which edits the Object and writes it back out to disk.

Apache vs Vendors, Attributing Blame

We brought the FileUpload issue to Apache's attention and they do not see it as a vulnerability. In their response to us, they stated:

"Having reviewed your report we have concluded that it does not represent a valid vulnerability in Apache Commons File Upload. If an application deserializes data from an untrusted source without filtering and/or validation that is an application vulnerability not a vulnerability in the library a potential attacker might leverage."

Tenable argued that if an application intends to deserialize DiskFileItems then they are still vulnerable to the altered object and they could have files written anywhere on their server, which seems to cross the privilege boundaries intended by the library based on the code.

Based on our research, there is no warning about distrusting this object included in their library, or mentioning the potential for problems. It seems like there could be a few lines of code to prevent the unintended aspect of this (files written to arbitrary locations), while still maintaining the functionality of the library. In doing that, it would add a layer of protection for companies that implement the library (which many do, and we are finding vulnerable).

After another Apache person mentioned that "java.io.File is serializable, too .. And, I assume that an intruder who manages to have a DiskFileItem created and getInputStream() invoked on it, can just as well create a File (or a String), and invoke new File(Input|Output)Stream?" We reminded them that the act of deserializing a DiskFileItem can cause arbitrary files to be written to disk. The attacker does not need to invoke a new outputstream because DiskFileItem's readObject() function has already done that for him. This is not expected behavior as best we can tell. This is also not at all like deserializing a Java.io.File. That said, we respect Apache's stance on this and are contacting vendors that implement the Commons FileUpload library in a way that makes their software vulnerable.


Per the Apache Software Foundation's response, it is up to each vendor/product that implements this library to ensure that it is done in such a manner that does not allow shenanigans.

Disclosure Timeline

2016-01-21 - Issue discovered
2016-01-25 - Issue reported to Apache via security@apache.org
2016-01-27 - Apache responds, "does not represent a vulnerability in Apache Commons FileUpload"
2016-01-27 - Tenable acknowledges mail, will report to vendors who implement in a vulnerable manner
2016-02-03 - MBelcher publishes FileUpload gadget for ysoserial
2016-03-05 - FileUpload gadget added to main ysoserial repository by Frohoff
2016-07-19 - DWF assigns new CVE ID

All information within TRA advisories is provided “as is”, without warranty of any kind, including the implied warranties of merchantability and fitness for a particular purpose, and with no guarantee of completeness, accuracy, or timeliness. Individuals and organizations are responsible for assessing the impact of any actual or potential security vulnerability.

Tenable takes product security very seriously. If you believe you have found a vulnerability in one of our products, we ask that you please work with us to quickly resolve it in order to protect customers. Tenable believes in responding quickly to such reports, maintaining communication with researchers, and providing a solution in short order.

For more details on submitting vulnerability information, please see our Vulnerability Reporting Guidelines page.

If you have questions or corrections about this advisory, please email advisories@tenable.com