
Глубокий технический анализ CVE-2023-46604, уязвимости RCE в протоколе OpenWire Apache ActiveMQ, включая разбор структуры пакетов и механизм эксплуатации.
ActiveMQ — это брокер, поддерживающий JMS и позволяющий осуществлять связь между системами, использующими не только Java, но и различные другие языки. Кроме того, это сервер с открытым исходным кодом для обмена сообщениями и шаблонами интеграции, который обеспечивает согласованность и устойчивость между системами благодаря функциям кластеризации, а также через базу данных и файловую систему.
OpenWire — это бинарный протокол обмена сообщениями, используемый в Apache ActiveMQ. Он разработан для эффективной передачи данных между брокером (ActiveMQ) и клиентами.
CVE-2023-46604 — это уязвимость в ActiveMQ, связанная с протоколом OpenWire (в процессе маршалинга). Удаленный злоумышленник, имеющий сетевой доступ к брокеру или клиенту OpenWire на основе Java, может манипулировать сериализованным типом класса в протоколе OpenWire, тем самым заставляя клиента или брокера (соответственно) инстанцировать любой класс, находящийся в classpath, что приводит к выполнению произвольных команд оболочки. Этот метод атаки характерен для APT-группы Andariel.
CVE-2023-46604 реализуется путем манипуляции XML-вызовами или внедрения вредоносных данных в процессе обмена данными через протокол OpenWire, что приводит к выполнению удаленных команд. Протокол OpenWire обрабатывает сериализацию и передачу данных, а уязвимость заключается в том, что в процессе сериализации допускается использование вредоносных типов классов.
[Рисунок] POC
[Рисунок] XML
Заголовок пакета
+----------------------------------------------------------------------------------+
| Длина пакета | Команда | Ид. команды | Требуется ответ на команду | CorrelationId |
|---------------|---------|------------|---------------------------|---------------|
| 00000066 | 1f | 00000000 | 00 | 00000000 |
+----------------------------------------------------------------------------------+
Тело пакета
+--------------------------------------------------------------------------------------+
| not-null | not-null | classname-size | classname | not-null | message-size | message |
|----------|----------|----------------|-----------|----------|--------------|---------|
| 01 | 01 | 0043 | ..... | 01 | 0012 | ..... |
+--------------------------------------------------------------------------------------+
Формат тела пакета OpenWire
[=Если not-null равен 1===========]
+----------+ [ +-------+----------------+ ]
| not-null | [ | size | encoded-string | ]
+----------+ [ +-------+----------------+ ]
| байт | [ | short | size октетов | ]
+----------+ [ +-------+----------------+ ]
[============================]
[Рисунок 1] Проверка пакета OpenWire в WireShark
[Рисунок 2] Значение заголовка пакета
Разбор заголовка пакета
+----------------------------------------------------------------------------------+
| Длина пакета | Команда | Ид. команды | Требуется ответ на команду | CorrelationId |
|---------------|---------|------------|---------------------------|---------------|
| 00000066 | 1f | 00000000 | 00 | 00000000 |
+----------------------------------------------------------------------------------+
Длина пакета : 00000066 / Протоколы типа OpenWire по умолчанию указывают длину пакета.
Команда : 1f / Устанавливается для обозначения ExceptionResponse(31). 31 в шестнадцатеричном виде — 1f.
Ид. команды : 00000000 / Имеет тип int (4 байта), в шестнадцатеричном представлении 00 00 00 00.
Требуется ответ на команду : 00 / Логический тип: True(01), False(00); здесь False.
CorrelationId : Тот же тип, что и Ид. команды.
[Рисунок 2] содержит пояснения к форме и назначению каждого поля заголовка пакета, чтобы облегчить понимание. Теперь, после рассмотрения заголовка, посмотрим, как тело пакета проходит через код.
[Рисунок 3] Значение тела пакета
Разбор содержимого тела пакета
+--------------------------------------------------------------------------------------+
| not-null | not-null | classname-size | classname | not-null | message-size | message |
|----------|----------|----------------|-----------|----------|--------------|---------|
| 01 | 01 | 0043 | ..... | 01 | 0012 | ..... |
+--------------------------------------------------------------------------------------+
not-null (01) | / Первая функция на [Рисунке 3] пропускает not-null
not-null (01) | classname-size (0043) | classname / Вторая функция на [Рисунке 3] пропускает not-null, третья функция проверяет размер
not-null (01) | message-size (0012) | message / Вторая функция на [Рисунке 3] пропускает not-null, третья функция проверяет размер
[Рисунок 3] показывает, как значения, установленные в теле пакета, влияют на процесс. В конечном итоге, через [Рисунок 4] сериализованные данные загружают класс и инстанцируют его.
[Рисунок 4] Выполнение RCE через createThrowable
[Рисунок 5] Пример выполнения RCE (калькулятор)
[Рисунок 6] Git Diff (5.17.2 -> 5.17.6)
[Рисунок 7] Функция validate
В отличие от других анализов, причина, по которой были проанализированы атакующие пакеты для данной уязвимости, заключалась в понимании и изучении того, как именно выполняется атака.
Тщательное изучение структуры и потока пакетов позволило четко определить конкретный метод, используемый злоумышленником для компрометации системы, и его механизм работы.