Back to articles
web servers
14 min readApril 23, 2026

25 Years of Email Server CVEs: Why the Same Bugs Keep Coming Back

Mail server vulnerabilities aren't a random walk. Look at twenty-five years of Postfix, Dovecot, Sendmail, and Exim CVEs and four bug classes account for almost everything. Here's why those classes keep recurring.

25 Years of Email Server CVEs: Why the Same Bugs Keep Coming Back

25 Years of Email Server CVEs: Why the Same Bugs Keep Coming Back

If you read CVE databases for a while, you start noticing that the bugs aren't new. They're the same bugs — different memory address, different decade, different codebase, but structurally identical to bugs that were already a known pattern when the affected code was written. Email servers are an unusually good case study for this because they've been around long enough (Sendmail since 1981, Postfix since 1998, Dovecot since 2002) for patterns to repeat several times.

This post walks through four bug classes that account for the vast majority of email-server CVEs, with one or two famous examples each. The goal isn't to be a CVE list — there are databases for that — it's to leave you with pattern recognition so the next mail-server CVE you read, you can place quickly: "ah, that's class three again."

A note: I'll cite CVE IDs where I'm confident in them. Verify any specific CVE ID before using this post as a primary source. CVE numbering occasionally gets rearranged, vendor advisories sometimes use different IDs than NVD, and the half-life of a "definitely real" CVE reference is shorter than you'd think.

Pattern 1: Parser disagreement

The pattern. Two implementations of the same protocol disagree on what some byte sequence means. An attacker straddles the boundary, sending bytes that the sending side parses one way and the receiving side parses another way. The gap is the vulnerability.

The famous example: SMTP smuggling, 2023. Disclosed by SEC Consult in December 2023, SMTP smuggling exploited disagreement between SMTP servers about what terminates the DATA section. The standard says

\r\n.\r\n
(CRLF dot CRLF). Many implementations historically also accepted
\n.\n
(bare LF dot bare LF) as a terminator, in the spirit of "be lenient with what you accept."

The attack: a sender writes a single SMTP DATA payload that contains an embedded

\n.\n
followed by a fully-formed second SMTP transaction. A strict receiver sees one message. A lenient receiver splits the payload at the embedded terminator and treats the trailing portion as a second authenticated message — typically inheriting the original session's authenticated state and trusted IP.

End result: an external attacker can inject mail that appears to originate from inside a victim's authenticated session. Bypasses SPF, DKIM (the signature is valid for the first message but the second message inherits the trusted disposition), DMARC alignment — the works.

CVE-2023-51764 (Postfix) is the canonical CVE for the receiving side. Sendmail, Exim, Cisco IronPort, and others got their own CVEs from the same disclosure. Postfix's mitigation is

smtpd_forbid_bare_newline = yes
, which became the default in subsequent versions. Patch your server, set the option explicitly, verify with a probe (post 8 covers how).

Other examples of parser disagreement:

  • HTTP request smuggling (the same pattern, different protocol)
  • Header parsing splits where one parser respects header folding and another doesn't
  • Address parser disagreements:
    Display Name <real@example.com>, attacker@evil.com
    parsed as one recipient by one server and two by another

Why it recurs. Network protocols are written in English by humans, not in formal specifications. Where the RFC is ambiguous, implementations make different choices. Where the RFC is clear, implementations sometimes deviate "for compatibility." The class of bug is structural — it can't be fixed by patching one implementation, only by aligning multiple implementations on identical strict parsing. That's slow.

Pattern 2: Pre-auth memory bugs

The pattern. A protocol parser running before authentication has a memory-safety bug — buffer overflow, integer overflow, off-by-one, double free, use-after-free. An attacker who can reach the listening port can trigger the bug without credentials. Severity is usually critical: pre-auth RCE on a network-facing service.

The famous example: Exim, "the Eximinator," CVE-2019-15846. A heap buffer overflow in Exim's TLS handshake handling allowed a remote attacker to execute code as root (Exim historically ran as root for a long time, which made the bug substantially worse). The bug was triggered during the TLS handshake — meaning before SMTP authentication, before even the SMTP protocol began. Just opening a TCP connection and starting a TLS handshake with a crafted SNI was enough.

Exim's history is unfortunately a recurring offender for this pattern. The codebase is older than Postfix, written in pre-modern C, with manual string handling pervasive. Multiple comparable CVEs have shipped over the years.

Postfix's record on this pattern is much better, in part because of Wietse Venema's discipline (he wrote a memory-safe string library for Postfix from the beginning) and in part because of the privilege separation we covered in post 2 — even if

smtpd
were exploitable, the chrooted unprivileged user is a much harder launchpad than Exim's historical root.

Dovecot has had pre-auth bugs. They've been less catastrophic because of Dovecot's architecture (the

dovenull
user, the chroot, the dropped capabilities) — analogous to why Postfix's record is good. But the parser surface in
imap-login
and
pop3-login
is real. Verify any specific CVE on cve.org or oss-security archives before citing.

Why it recurs. C and C++ remain the dominant languages for high-performance network daemons, and writing memory-safe C requires sustained discipline that doesn't survive twenty-year codebases with multiple maintainers. Modern code review and fuzzing have reduced the rate, but every parser change is a chance to ship the next variant.

Pattern 3: TLS state machine bugs

The pattern. A protocol that supports STARTTLS — upgrading an existing plaintext connection to TLS — does the upgrade incorrectly. Specifically, it fails to discard pre-handshake bytes, fails to verify identity, or processes commands across the upgrade boundary as though they came from the post-upgrade authenticated state.

The famous example: STARTTLS plaintext command injection. This is a class of bug, not one CVE. Postfix had it (CVE-2011-0411 and related). Sendmail had it. Cyrus IMAP had it. The mechanism: an attacker establishes a plaintext connection, sends

STARTTLS\r\n
followed immediately by
AUTH PLAIN <attacker-supplied-credentials>\r\n
— both in the same TCP write, before the TLS handshake completes. A buggy server reads the entire buffer, processes
STARTTLS
, does the TLS handshake, and then continues reading from the same buffer — finding the pre-injected
AUTH PLAIN
line and processing it as if it came from inside the now-encrypted session.

The fix is one line: when STARTTLS succeeds, empty the read buffer. Discard any bytes that arrived before the handshake completed. Multiple servers shipped without this for years.

Why it recurs. The bug is invisible in code review unless you're looking specifically for buffer-handling at protocol-state boundaries. It looks like normal "read line, dispatch line" logic. The only visible artifact is what doesn't happen: the buffer flush. Tests that exercise STARTTLS look like a happy-path connection — pre-injection requires sending malformed-by-design traffic.

This pattern also includes:

  • STARTTLS stripping (covered in post 4) where an attacker removes the STARTTLS offer from the EHLO response
  • STARTTLS downgrade where weak ciphers or old TLS versions are accepted, allowing a MITM to weaken the channel
  • Misvalidated server certificates on the client side of SMTP (Postfix's
    smtp
    daemon talking to peers) — historically, MTA-to-MTA TLS was loosely validated by default

Pattern 4: Misuse of restrictions ordering

The pattern. Postfix's

smtpd_*_restrictions
lists are evaluated in order, with the first matching rule winning. Reorder them and you change behavior in ways that aren't always visible until the wrong message gets relayed. This is technically a config-vuln class, but it's so commonly exploitable on real-world deployments that it deserves its own pattern slot.

The classic mistake. A config that started life as:

smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    reject_rbl_client zen.spamhaus.org

…is fine. Local networks and authenticated users can relay; everyone else is rejected before the RBL check (which is a feature — RBL lookups are expensive, skip them for trusted senders).

A future admin adds a rule and accidentally puts it before

reject_unauth_destination
:

smtpd_recipient_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    check_recipient_access hash:/etc/postfix/recipient_overrides,
    reject_unauth_destination,
    reject_rbl_client zen.spamhaus.org

If

recipient_overrides
returns
OK
for some recipient (typically a postmaster or alias), that
OK
terminates the restriction list — meaning
reject_unauth_destination
is never evaluated for that recipient, which means anyone in the world can relay to that address. Which means anyone in the world can relay through your server using that address as a stepping stone.

The modern Postfix mitigation. Postfix 2.10+ separated

smtpd_relay_restrictions
(specifically for "is this allowed to relay?") from
smtpd_recipient_restrictions
(for general per-recipient policy).
smtpd_relay_restrictions
is evaluated first, so the relay decision is harder to bypass with a misordered recipient rule. Configs migrated from older versions often still have all the logic in
smtpd_recipient_restrictions
and inherit the older risk.

Why it recurs. Order-sensitive lists in config files are a footgun. Every popular configuration system that has them — Postfix's restriction lists, Apache's

Order Allow,Deny
, iptables rules, AWS IAM policies — has produced the same class of misconfiguration repeatedly. The fix at the framework level is to make the safer behavior the default; the fix at the operator level is to write tests against the configuration (more on this in post 8).

Why the same bugs keep coming

Four reasons, ranked by importance.

Protocol age. SMTP, POP3, and IMAP were designed when "be liberal in what you accept" was considered virtue. That posture has aged badly — every "lenient" parser is a future smuggling bug. Modern protocol design (HTTP/2, gRPC) emphasises strict parsing, but mail protocols can't be retrofitted without losing interoperability with the long tail of old implementations.

RFC ambiguity. Where two RFCs disagree (or one RFC contradicts itself in different sections), implementations choose. Different implementations choose differently. The gap is exploitable.

C codebases. The dominant mail servers are written in C. Memory safety in C requires sustained discipline. Most of the catastrophic CVEs in the email server world are memory bugs in C parsers.

Target value. Mail servers are everywhere. Compromising one means access to communications, account-recovery flows for every other service, and a launchpad for further attacks. The economic incentive to find bugs in Postfix and Dovecot is high.

The case for memory-safe rewrites

There have been several attempts at memory-safe mail server implementations. OpenSMTPD (from the OpenBSD project) is C, but the codebase is small, careful, and has had a much better security record than Sendmail or Exim. Maddy is a pure-Go all-in-one mail server. There are at least two Rust SMTP server projects in active development as of 2026.

Why hasn't any of them displaced Postfix? A few reasons:

  • Postfix is good. Wietse's discipline was not normal for the era, and Postfix's bug rate per line of C is unusually low.
  • Migration costs are huge. Mail server configs are intricate; users don't migrate without strong cause.
  • Network effects: every tutorial, every monitoring check, every operator's muscle memory targets Postfix and Dovecot.

The likely future is incremental — Postfix and Dovecot have both adopted modern fuzzing, sanitizer-based testing, and stricter parsing defaults. The bug rate is dropping, just slowly.

What this means for an operator

If you're running Postfix and Dovecot in 2026, the practical takeaways are:

  1. Patch quickly. Email server CVEs aren't theoretical. SMTP smuggling went from disclosure to active exploitation in days. Subscribe to the relevant security mailing lists.
  2. Set strict parser options.
    smtpd_forbid_bare_newline = yes
    on Postfix. Strict TLS minimums (
    smtpd_tls_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1
    ). Strict STARTTLS enforcement on submission ports.
  3. Use the architecture. Postfix's chroot and unprivileged users are there for a reason — don't disable them in pursuit of "simpler" configs. Dovecot's
    dovenull
    user and login-process restrictions are likewise non-optional.
  4. Audit your restrictions ordering. Specifically, make sure
    smtpd_relay_restrictions
    exists and contains your relay logic (not buried in
    smtpd_recipient_restrictions
    ). Post 7 covers this in detail.
  5. Assume the next CVE is coming. Don't run your mail server like an appliance. Patch, monitor, audit. The bugs that exist now might not be public — but they're coming.

Post 7 covers the operator-error misconfigurations that are essentially "self-inflicted CVEs" — bugs in your config rather than bugs in the code, but with the same exploitable consequences. Post 8 turns it around and walks through how to test your own server for all of the above.

What I'd Tell My Past Self

The thing the CVE history actually teaches isn't "patch quickly" (everyone knows that). It's the bugs aren't a personal failing of any one project. The same bug classes appear in Postfix, Dovecot, Exim, Sendmail, Cyrus, Microsoft Exchange, Cisco IronPort. The fault is structural — old protocols, ambiguous specs, unsafe languages, high-value targets. Operators who blame their mail server for being insecure are missing the point. Email is insecure. Postfix is one of the better-defended footholds in an inherently messy landscape. Treat your mail server with the respect that situation demands.

Discussion

0 comments

Share your thoughts

No comments yet. Be the first to share your thoughts!