
분석 cve-2024-42327
대상: 10.129.231.176
정보: 내 대상은 Zabbix 서버입니다. Zabbix에 로그인하기 위해 기본 사용자 계정을 받았습니다: 사용자 matthew 비밀번호 96qzn0h2e1k3. 이 계정은 기본 사용자이며 추가 그룹이나 권한이 없습니다.
평소와 같이 열거부터 시작합니다. nmap을 사용하여 포트 스캔을 수행합니다.

nmap 출력은 기본 SSH 포트와 Apache2도 기본 포트에 있음을 보여줍니다. 또한 Zabbix 서비스를 실행 중인 10051 및 10050 포트가 있습니다.
브라우저 URL에 IP를 입력하고 기본 HTTP 포트인 80번 포트로 Zabbix에 접속합니다.

이것은 Zabbix 로그인 화면입니다. 받은 사용자로 로그인합니다.


바닥글에서 Zabbix 버전을 찾았습니다:

구글신을 사용하여 이 Zabbix 버전에 CVE가 있는지 검색했습니다.

오랜 검색 끝에 이 버전이 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 문서에는 API를 호출하기 위한 HTTP 요청 방법이 설명되어 있습니다.

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에 검증이 전혀 없으며, 이로 인해 한 번에 여러 그룹에 자신을 추가하는 데 악용될 수 있습니다. 그룹이 비활성화되지 않고 GUI 접근이 허용되는 한, 이 취약점을 악용하여 현재 역할을 변경할 수 있습니다. 사용자 ID 3은 matthew이며, 사용자 그룹 7은 Zabbix administrators 그룹이고 사용자 그룹 13은 Internal 그룹으로, 둘 다 제한 없는 권한을 가지고 있습니다. 응답은 변경이 성공했음을 나타냅니다.
이 검증 부재를 이용하여 권한 상승을 시도합니다. 페이로드를 편집하고 요청을 다시 보냈습니다.

userid 3은 사용자 matthew의 ID를 나타냅니다. usrgrps에는 그룹 ID 목록이 포함됩니다: 13은 Internal 그룹이고 7은 Zabbix administrators 그룹입니다. 서버 응답은 작업 성공을 확인합니다:
{"jsonrpc":"2.0","result":{"userids":["3"]},"id":1}
이제 현재 사용자의 사용자 그룹을 추출할 수 있습니다. 요청을 수정하여 다시 보냅니다.

응답을 확인하면 ID 3의 사용자가 Internal 및 Zabbix administrators 그룹에 있음을 알 수 있습니다.
{"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 administrators 그룹에 할당된 시나리오에서는 아이템 생성을 활용하여 원격 코드 실행을 트리거할 수 있으며, 이는 다음 CVE에서 다룹니다.
CUser 클래스의 소스 코드를 다시 분석하여 user.get 함수를 68번째 줄에서 조사합니다. 108번째 줄에는 다음 코드로 확인이 있습니다:
// permission check
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
if (!$options['editable']) {
$sqlParts['from']['users_groups'] = 'users_groups ug';
$sqlParts['where']['uug'] = 'u.userid=ug.userid';
$sqlParts['where'][] = 'ug.usrgrpid IN ('.
' SELECT uug.usrgrpid'.
' FROM users_groups uug'.
' WHERE uug.userid='.self::$userData['userid'].
')';
}
else {
$sqlParts['where'][] = 'u.userid='.self::$userData['userid'];
}
}
이 코드에서 API 요청에 editable 옵션이 제공되면 사용자 그룹을 검증하는 대신 현재 사용자 ID가 현재 사용자와 일치하는지만 확인하므로 user.get 함수 사용 시 권한을 무시합니다. 234번째 줄에서 addRelatedObjects가 호출되는데, 이는 SQL 인젝션에 취약한 함수입니다. addRelatedObject 함수를 2969번째 줄에서 분석하면 대부분의 SQL 문이 안전해 보이지만 3041번째 줄에 도달하면 문제가 발생합니다.
// adding user role
if ($options['selectRole'] !== null && $options['selectRole'] !==
API_OUTPUT_COUNT) {
if ($options['selectRole'] === API_OUTPUT_EXTEND) {
$options['selectRole'] = ['roleid', 'name', 'type', 'readonly'];
}
$db_roles = DBselect(
'SELECT u.userid'.($options['selectRole'] ? ',r.'.implode(',r.',
$options['selectRole']) : '').
' FROM users u,role r'.
' WHERE u.roleid=r.roleid'.
' AND '.dbConditionInt('u.userid', $userIds)
);
foreach ($result as $userid => $user) {
$result[$userid]['role'] = [];
}
while ($db_role = DBfetch($db_roles)) {
$userid = $db_role['userid'];
unset($db_role['userid']);
$result[$userid]['role'] = $db_role;
}
}
return $result;