
writeup cve-2024-42327
Target: 10.129.231.176
Information: I know my target is a Zabbix server. I received a default user account to log into Zabbix: user matthew passwd 96qzn0h2e1k3. This account is a default user, without additional groups or privileges.
As usual, we start with enumeration, let's do a port scan using nmap.

The nmap output shows the default SSH port and Apache2 also on the default port. We also have ports 10051 and 10050 running some Zabbix service.
Let's access Zabbix by entering the IP in the browser URL and the default HTTP port, port 80.

This is the Zabbix login screen, I'll log in with the user I received.


In the footer, I found the Zabbix version:

Using the 'father of fools' (Google), I searched if there was any CVE for this Zabbix version.

After a long time of research, I saw that this version is vulnerable to CVE-2024-42327 which is about SQL injection exploitation to obtain database data and escalate privileges, and to CVE-2024-36467 which allows changing the user role to superuser by abusing missing access controls.
https://nvd.nist.gov/vuln/detail/CVE-2024-36467
https://nvd.nist.gov/vuln/detail/CVE-2024-42327
The Zabbix documentation teaches how to make HTTP requests to call the API.

https://www.zabbix.com/documentation/current/en/manual/api
I sent the request calling apiinfo.version as taught in the documentation.

which returned the following:
{"jsonrpc":"2.0","result":"7.0.0","id":1}
For the next test, I changed some parameters in this request to send again.

In method, I changed from apiinfo.version to user.login and added the parameters username and password. I also saw this in the Zabbix documentation.

It returned a token:
{"jsonrpc":"2.0","result":"9566174b00c9c3ca552abc1a52d670ba","id":1}
After more time researching, I decided to go to the Zabbix repository on GitHub.
https://github.com/zabbix/zabbix
I searched for CUser and found a file CUser.php.

We found the user.update function:
public function update(array $users) {
$this->validateUpdate($users, $db_users);
self::updateForce($users, $db_users);
return ['userids' => array_column($users, 'userid')];
}
I didn't find any authorization checks, so I decided to change my function to a superuser function, went back to the request and made adjustments to the payload.

It returned an error with an invalid params message.
After another long analysis of the code, we found this function
/**
* 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;
}
}
}
According to this snippet, we cannot change our roles because our role is checked by extracting our data from the API token and verifying in the database if we are that user. But analyzing the code, we see that usrgrps has no validation at all, and because of this lack of validation, it can be abused to add ourselves to multiple groups at once. There is no check to prevent a user from adding themselves to groups they should not have access to.
Let's try to escalate privileges due to this lack of validation, I edited the payload and sent the request again.

userid 3 refers to the id of user matthew
usrgrps contains a list of group IDs: 13 which is an internal group and 7 is the Zabbix administrators group. Our server response confirms the success of the operation:
{"jsonrpc":"2.0","result":{"userids":["3"]},"id":1}
Now we can extract the user groups of our current user. Let's modify the request and send it again.

When checking the response, we see that the user with ID 3 is in the Internal and Zabbix administrators groups.
{"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}
In a scenario where a valid Host Group was assigned to the Zabbix administrators group, they can leverage item creation to trigger remote code execution, which will be covered in the next CVE.
Analyzing the source code in the CUser class again, we investigate the user.get function at line 68. Line 108 contains a check with the following code: