
разбор cve-2024-42327
цель: 10.129.231.176
Информация: я знаю, что моя цель — сервер Zabbix. Я получил стандартную учётную запись пользователя для входа в Zabbix: пользователь matthew, пароль 96qzn0h2e1k3. Эта учётная запись имеет стандартного пользователя, без групп или дополнительных привилегий.
Как обычно, начнём с перечисления, сделаем сканирование портов с помощью nmap.

Вывод nmap показывает нам, что стандартный порт SSH и Apache2 также на стандартном порту. Также есть порты 10051 и 10050, на которых работает какой-то сервис Zabbix.
Зайдём в Zabbix, указав IP в URL браузера на стандартном HTTP-порту, порту 80.

Это экран входа в Zabbix. Войду с полученным пользователем.


В нижнем колонтитуле я нашёл версию Zabbix:

Используя «отца всех дураков» (Google), я поискал, есть ли уже какой-нибудь CVE для этой версии Zabbix.

После длительного поиска я увидел, что эта версия уязвима к CVE-2024-42327, который описывает эксплуатацию SQL-инъекции для получения данных из базы данных и повышения привилегий, и к CVE-2024-36467, который позволяет изменить роль пользователя на суперпользователя, злоупотребляя отсутствием контроля доступа.
https://nvd.nist.gov/vuln/detail/CVE-2024-36467
https://nvd.nist.gov/vuln/detail/CVE-2024-42327
В документации Zabbix есть инструкции, как делать HTTP-запросы для вызова API.

https://www.zabbix.com/documentation/current/en/manual/api
Я отправил запрос, вызывающий apiinfo.version, как написано в документации.

Что вернуло следующее:
{"jsonrpc":"2.0","result":"7.0.0","id":1}
Для следующего теста я изменил несколько параметров в этом запросе и отправил снова.

В method я изменил apiinfo.version на user.login и добавил параметры username и password. Это я тоже видел в документации Zabbix.

Вернулся токен:
{"jsonrpc":"2.0","result":"9566174b00c9c3ca552abc1a52d670ba","id":1}
После ещё некоторого времени поисков я решил зайти в репозиторий Zabbix на GitHub.
https://github.com/zabbix/zabbix
Я поискал CUser и нашёл файл CUser.php.

Мы нашли функцию user.update:
public function update(array $users) {
$this->validateUpdate($users, $db_users);
self::updateForce($users, $db_users);
return ['userids' => array_column($users, 'userid')];
}
Я не нашёл никакой проверки авторизации, поэтому решил изменить свою роль на роль суперпользователя. Вернулся к запросу и подправил полезную нагрузку.

Вернулась ошибка с сообщением invalid params.
Проведя ещё один долгий анализ кода, мы нашли эту функцию:
/**
* Additional check to exclude an opportunity to deactivate himself.
*
* @param array $users
* @param array $users[]['usrgrps'] (optional)
*
From this snippet, we understand that we cannot change our roles because our role is checked
from extracting our data from the API token, and verifying against the database if we are that user.
But following the code we see that usrgrps has no validation at all, and therefore can be abused
to add ourselves into multiple groups at once. As long as the group is not disabled and the group
allows GUI access we can abuse this to change our current role with the following command:
User ID 3 is matthew , User group 7 is the Zabbix administrators group and user group 13 is the
Internal group which both hold unrestrictive privileges. The response indicates that the change
was successful:
* @throws APIException
*/
private function checkHimself(array $users) {
foreach ($users as $user) {
if (bccomp($user['userid'], self::$userData['userid']) == 0) {
if (array_key_exists('roleid', $user) && $user['roleid'] !=
self::$userData['roleid']) {
self::exception(ZBX_API_ERROR_PARAMETERS, _('User cannot change
own role.'));
}
if (array_key_exists('usrgrps', $user)) {
$db_usrgrps = DB::select('usrgrp', [
'output' => ['gui_access', 'users_status'],
'usrgrpids' => zbx_objectValues($user['usrgrps'], 'usrgrpid')
]);
foreach ($db_usrgrps as $db_usrgrp) {
if ($db_usrgrp['gui_access'] == GROUP_GUI_ACCESS_DISABLED
|| $db_usrgrp['users_status'] ==
GROUP_STATUS_DISABLED) {
self::exception(ZBX_API_ERROR_PARAMETERS,
_('User cannot add himself to a disabled group or a
group with disabled GUI access.')
);
}
}
}
break;
}
}
}
Согласно этому фрагменту, мы не можем изменить свои роли, потому что наша роль проверяется путём извлечения наших данных из токена API и проверки в базе данных, являемся ли мы этим пользователем. Но анализируя код, мы видим, что usrgrps вообще не имеет проверки, и из-за этого отсутствия проверки им можно злоупотребить, чтобы добавить себя сразу в несколько групп. Нет никакой проверки, которая бы помешала пользователю добавить себя в группы, к которым у него не должно быть доступа.
Попробуем повысить привилегии, используя это отсутствие проверки. Я отредактировал полезную нагрузку и отправил запрос снова.

userid 3 относится к ID пользователя matthew
usrgrps содержит список ID групп: 13 — это группа Internal, а 7 — группа Zabbix administrators. Ответ сервера подтверждает успешность операции:
{"jsonrpc":"2.0","result":{"userids":["3"]},"id":1}
Теперь мы можем извлечь группы пользователей для нашего текущего пользователя. Изменим запрос и отправим снова.

Проверив ответ, мы видим, что пользователь с ID 3 находится в группах администраторов Internal и Zabbix.
{"jsonrpc":"2.0","result":[{"userid":"1","usrgrps":
[{"usrgrpid":"7","name":"Zabbix administrators"},
{"usrgrpid":"13","name":"Internal"}]},{"userid":"2","usrgrps":
[{"usrgrpid":"8","name":"Guests"}]},{"userid":"3","usrgrps":
[{"usrgrpid":"7","name":"Zabbix administrators"},
{"usrgrpid":"13","name":"Internal"}]}],"id":1}
В сценарии, когда группе администраторов Zabbix была назначена допустимая группа хостов, они смогут использовать создание элементов для запуска удаленного выполнения кода, что будет рассмотрено в следующем CVE.
Анализируя исходный код в классе CUser снова, мы исследовали функцию user.get на строке 68. Строка 108 содержит проверку со следующим кодом: