
Cacti 1.2.22 — неаутентифицированная инъекция команд
| Поле | Детали |
|---|
| Продукт | Cacti |
| Затронутая версия | 1.2.22 |
| Уязвимость | Неаутентифицированная инъекция OS-команд |
| CVE | CVE-2022-46169 |
| CWE | CWE-77 — Инъекция команд |
| Серьёзность | Критическая |
| CVSS v3.1 | 9.8 |
| Вектор атаки | Сеть |
| Аутентификация | Не требуется |
| Взаимодействие с пользователем | Не требуется |
| Воздействие | Конфиденциальность / Целостность / Доступность |
| Исправленная версия | 1.2.23 |
| Уязвимый компонент | remote_agent.php |
| Дополнительный компонент | lib/functions.php |
| Основное уязвимое действие | polldata |
Опубликованный вектор CVSS:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Официальный бюллетень оценивает её в 9.8 Критическая. (GitHub)
На самом деле здесь две ошибки, объединённые в цепочку.
Этап 1 — Обход авторизации
remote_agent.php в Cacti принимает запросы без обычной аутентификации, но пытается определить, является ли запрашивающая сторона авторизованным поллером.
Поток авторизации фактически выглядит так:
HTTP-запрос
│
▼
remote_agent.php
│
▼
remote_client_authorized()
│
▼
get_client_addr()
│
▼
gethostbyaddr()
│
▼
сравнение имени хоста с таблицей poller
Проблема в get_client_addr().
В версии 1.2.22 она проверяет множество серверных переменных, производных от HTTP, включая заголовки forwarded-client-IP. Бюллетень объясняет, что управляемые атакующим значения HTTP_* могут влиять на IP, возвращаемый этой функцией. (GitHub)
Это означает, что приложение можно обмануть, заставив его поверить:
Атакующий
↓
"мой IP — это сервер Cacti"
↓
gethostbyaddr()
↓
имя хоста сервера Cacti
↓
совпадает с таблицей poller
↓
АВТОРИЗОВАН
Таким образом, атакующему не нужна легитимная учётная запись Cacti.
После обхода проверки авторизации интересующая функциональность конечной точки — это действие polldata.
Соответствующий путь выполнения примерно таков:
remote_agent.php
│
▼
polldata
│
▼
poll_for_data()
│
├── host_id
├── local_data_ids
└── poller_id
│
▼
поиск poller_item
│
▼
POLLER_ACTION_SCRIPT_PHP
│
▼
proc_open()
│
▼
выполнение OS-команды
Важная ошибка — обработка poller_id.
Приложение получает его с помощью:
get_nfilter_request_var()
вместо того чтобы обеспечивать его целочисленность.
Это управляемое атакующим значение в конечном итоге становится частью команды, передаваемой в PHP-функцию proc_open(). Официальный бюллетень прямо называет это примитивом инъекции команд. (GitHub)
Концептуально:
управляемый атакующим ввод
↓
poller_id
↓
конкатенация строк
↓
proc_open()
↓
команда операционной системы
Это критическая часть уязвимости.
Интересный момент для вашего анализа PoC в том, что ни одна из ошибок по отдельности не является всей историей.
Это цепочка уязвимостей:
┌──────────────────────────┐
│ Неаутентифицированный │
│ атакующий │
└────────────┬─────────────┘
│
▼
remote_agent.php
│
▼
Обход авторизации
через логику IP клиента
│
▼
polldata
│
▼
поиск poller_item
│
▼
POLLER_ACTION_SCRIPT_PHP
│
▼
ввод атакующего
→ poller_id
│
▼
proc_open()
│
▼
Выполнение команд
│
▼
RCE
Это очень важное различие, которое стоит сделать в вашем отчёте:
CVE-2022-46169 — это не просто "плохой параметр в remote_agent.php". Это объединённая в цепочку уязвимость обхода авторизации + инъекции команд.
Официальный бюллетень подтверждает, что уязвимое условие выполнения требует poller_item, действие которого — POLLER_ACTION_SCRIPT_PHP. (GitHub)
Ваш PoC должен явно документировать это, поскольку это важная аналитическая деталь.
Целевой системе нужен подходящий poller_item, настроенный с:
POLLER_ACTION_SCRIPT_PHP
Бюллетень Cacti отмечает, что это распространено на производственных установках, поскольку предопределённые шаблоны, такие как Device - Uptime и Device - Polling Time, могут создавать такие записи. (GitHub)
Поэтому не пишите:
"Каждая установка Cacti 1.2.22 автоматически эксплуатируема."
Более технически точное утверждение:
Cacti 1.2.22 уязвим, и успешное выполнение команд зависит от наличия соответствующей конфигурации
poller_item.
host_id и local_data_id важныpoll_for_data() не просто выполняет переданный poller_id.
Сначала она запрашивает poller_item, используя значения, соответствующие:
host_id
local_data_id
Затем она проверяет действие полученного элемента.
Уязвимое условие фактически таково:
host_id
+
local_data_id
↓
poller_item
↓
action == POLLER_ACTION_SCRIPT_PHP
↓
уязвимый путь выполнения
Оригинальный бюллетень отмечает, что эти идентификаторы можно обнаружить, поскольку соответствующие записи существуют в базе данных приложения, и что подходящие записи, скорее всего, существуют на производственных установках. (GitHub)
Для публичного PoC я бы продемонстрировал это предварительное условие явно, а не скрывал его.
Для публикуемого материала я рекомендую сделать PoC, демонстрирующий выполнение команд без предоставления читателям готового weaponized reverse-shell пейлоада.
Например, структурируйте демонстрацию так:
1. Разверните Cacti 1.2.22 в изолированной ВМ
2. Настройте poller_item с POLLER_ACTION_SCRIPT_PHP
3. Подтвердите доступность remote_agent.php
4. Продемонстрируйте влияние на решение об авторизации
5. Достигните пути выполнения polldata
6. Используйте безвредный маркер выполнения команд
7. Зафиксируйте результирующие доказательства приложения/журналов
8. Обновитесь до 1.2.23
9. Повторите тест
10. Продемонстрируйте, что уязвимость больше не эксплуатируема
Это даёт легитимную демонстрацию уязвимости, не превращая отчёт в готовое интернет-оружие RCE.
Вы можете разделить уязвимый код на три области.
remote_agent.phpОтвечает за предоставление функциональности удалённого агента и диспетчеризацию запрошенного действия.
remote_agent.php
│
└── action = polldata
│
▼
poll_for_data()
lib/functions.phpСодержит get_client_addr().
Проблемный дизайн заключается в доверии значениям, производным от HTTP, при определении фактического сетевого адреса запрашивающей стороны.
Официальный бюллетень перечисляет несколько связанных с HTTP переменных, которые проверяются перед возвратом к фактическому удалённому адресу. (GitHub)
proc_open()Финальная опасная точка — конструирование PHP-команды, выполняемой через proc_open().
Бюллетень определяет уязвимый поток как:
poller_id
↓
get_nfilter_request_var()
↓
конструирование команды
↓
proc_open()
(GitHub)
Бюллетень Cacti определяет два важных изменения для устранения уязвимости.
Во-первых, poller_id следует обрабатывать как целое число:
get_nfilter_request_var()
↓
get_filter_request_var()
Во-вторых, значение должно дополнительно экранироваться для оболочки перед включением в команду:
escapeshellarg($poller_id)
Бюллетень конкретно рекомендует обе меры. (GitHub)
Сторона авторизации также должна перестать позволять атакующему произвольно влиять на IP клиента, используемый для авторизации. (GitHub)
Cacti 1.2.22
│
├── уязвима
├── обход авторизации
├── инъекция команд
└── потенциальный неаутентифицированный RCE
Cacti 1.2.23
│
└── CVE-2022-46169 исправлена
Бюллетень вендора указывает 1.2.22 как затронутую и 1.2.23 как исправленный релиз 1.2.x. (GitHub)
Бюллетень безопасности Amazon независимо описывает проблему как позволяющую неаутентифицированному атакующему выполнять произвольные команды и присваивает ей оценку CVSS 9.8. (AWS Training and Certification)
Я бы использовал что-то вроде:
CVE-2022-46169 — Неаутентифицированная инъекция команд / RCE в Cacti 1.2.22
Или более техническое:
CVE-2022-46169: Анализ цепочки обхода авторизации и инъекции команд в удалённом агенте Cacti 1.2.22
А сводка уязвимости может быть такой:
Cacti 1.2.22 содержит критическую цепочку уязвимостей в функциональности удалённого агента. Атакующий может манипулировать логикой определения адреса клиента, чтобы обойти проверку авторизации удалённого агента. После достижения функциональности
polldataнедостаточная проверка параметраpoller_idпозволяет данным, управляемым атакующим, достичь команды, выполняемой черезproc_open(). При конфигурациях, содержащих элемент поллераPOLLER_ACTION_SCRIPT_PHP, это может привести к неаутентифицированному удалённому выполнению команд. Проблема была исправлена в Cacti 1.2.23. (GitHub)
Одна важная поправка для вашего исследования: не путайте это с CVE-2023-39362. Эта более поздняя RCE в Cacti затрагивает 1.2.24 и была исправлена в 1.2.25, так что это не та уязвимость, которая нужна для PoC на 1.2.22. (NVD)