CVE-2026-45185 Dead.Letter: one byte into freed memory — and your mail server belongs to someone else
CVE-2026-45185 Dead.Letter: one byte into freed memory — and your mail server belongs to someone else
Exim accepted a TLS connection. The client started transmitting a message body via BDAT, then abruptly sent a TLS close_notify — the session teardown signal — mid-transfer. Exim freed the buffer. But the nested BDAT receive handler kept reading bytes and called ungetc(), writing a single \n character into already-freed memory. One byte. Into allocator metadata. That’s enough for unauthenticated RCE.
CVE-2026-45185, named Dead.Letter, is a use-after-free in Exim’s BDAT parser when built against GnuTLS. CVSS 9.8, CWE-416 (Use After Free). Affected versions: 4.97 through 4.99.2, compiled with USE_GNUTLS=yes. The patch shipped May 12, 2026 in version 4.99.3. A PoC is public; no confirmed in-the-wild exploitation was reported at patch time — but that was three weeks ago.
The vulnerability was found by Federico Kirschbaum of XBOW Security, who by his own admission had never read a line of Exim’s source before. That’s what makes the story interesting: this bug had been hiding in plain sight for nine years.
WHAT IS EXIM AND BDAT
Exim is one of the most widely deployed Mail Transfer Agents on Unix systems. Debian, Ubuntu, and their derivatives ship Exim as the default MTA, and most of those builds use GnuTLS as the TLS library. That means millions of mail servers worldwide are potentially affected — simply because they run a Debian-based distribution.
BDAT is the SMTP CHUNKING extension (RFC 3030). Instead of the traditional DATA command, which ends the message body with a lone dot on a line, BDAT lets the client transmit the body in chunks with an explicit size: BDAT 1234, BDAT 5678 LAST. It’s more efficient for large messages and enabled by default in modern Exim — no special configuration required.
The key thing to understand is the layering: when Exim runs over TLS, there are two distinct levels — the TLS session, which handles encryption through GnuTLS, and the SMTP level, which parses commands and message bodies. The bug lives at the seam between these two layers, triggered when TLS tears down before the SMTP handler has finished its work.
HOW THE BUG WORKS
After a successful TLS handshake, Exim allocates a transfer buffer via store_malloc() and replaces the standard SMTP receive functions with TLS wrappers. This buffer — xfer_buffer — lives inside the GnuTLS session state and gets freed when the TLS connection closes.
The vulnerable sequence goes like this: the client establishes a TLS connection, sends BDAT, and starts transmitting the message body. Mid-transfer — before sending the final chunk with the LAST flag — the client sends a TLS close_notify. GnuTLS receives the teardown signal and frees all associated resources, including xfer_buffer. The nested BDAT handler doesn’t know any of this: it’s still waiting for the final byte, and it calls ungetc('\n', ...), writing a newline character into the already-freed memory region.
That single byte lands on Exim’s allocator metadata — the internal structures that track which memory blocks are free, which are in use, and how large they are. Writing into allocator metadata isn’t just memory corruption; it’s an opportunity to control how subsequent malloc() and free() calls behave. That’s the door to further exploitation.
HOW IT IS EXPLOITED
A one-byte write into allocator metadata is a weak primitive — but sufficient. As Kirschbaum describes in the writeup, that write becomes the foundation for stronger primitives: controlled writes to arbitrary addresses, which ultimately allow overwriting a function pointer inside the Exim process.
All it takes to trigger the bug is a TLS connection with BDAT support — both are standard on any modern GnuTLS-based Exim. No credentials, no prior knowledge of the server, nothing non-standard. The only requirement is that Exim was built against GnuTLS, which is the default on Debian and Ubuntu.
During the responsible disclosure window, XBOW tested how far exploit development could actually go — both manually and using autonomous tooling. They describe the results as convincing enough to classify this as a practically exploitable RCE, not a theoretical heap corruption.
WHAT HAPPENS NEXT — DEPENDS ON CONFIGURATION
Exim typically runs as an unprivileged user — Debian-exim or equivalent. If the systemd service unit isn’t hardened with PrivateTmp, NoNewPrivileges, and capability restrictions, an attacker gets a shell as that user. What happens from there depends on the rest of the system’s configuration.
On many servers, though, Exim runs as root for certain operations — local mail delivery through setuid binaries, for instance. Without process isolation via systemd or Linux namespaces, a compromised Exim process can mean full root access. Check what user the process is actually running as:
ps aux | grep exim
The first column is the user. If it’s root, a successful exploit hands the attacker root immediately. If it’s Debian-exim or another unprivileged account, the damage is contained to that process’s permissions — but the RCE still happened.
If your Exim is configured purely as a relay and doesn’t accept inbound mail from arbitrary external clients, the practical risk is lower. But if ports 25, 465, or 587 are open to the internet and the Exim version is vulnerable, there’s nothing standing between an attacker and the bug.
REAL-WORLD ATTACK CHAIN
Reconnaissance is fast. The attacker scans port 25, reads the SMTP banner for the Exim version, sends EHLO, and checks the extension list. If the server responds with 250-CHUNKING, BDAT is available. If the banner or other fingerprinting confirms a version in the 4.97–4.99.2 range, the server is a target. None of this requires credentials or special tooling — it takes seconds.
From there: establish a TLS connection, start a BDAT transfer, send close_notify mid-body, follow with a final byte in cleartext. Exim writes \n into freed memory. The server either crashes — confirming the vulnerability — or, with the right heap layout, keeps running with corrupted allocator metadata, opening the path to full exploitation.
The stretch from heap corruption to actual RCE is what XBOW explored during the disclosure window. It’s not trivial: you need to understand Exim’s allocator behavior under specific allocation patterns, build stronger primitives on top of the weak single-byte write, and find a path to overwriting a function pointer. Kirschbaum publicly described it as achievable, and XBOW verified this both manually and with autonomous tools. A ready-to-run shell exploit wasn’t public at disclosure time — but that’s a matter of time and attacker motivation.
TIMELINE
On May 1, 2026, Federico Kirschbaum of XBOW Security submitted the initial report to the Exim team. It came out of their autonomous vulnerability research tooling — notably, Kirschbaum had never worked with Exim’s codebase before.
On May 4, Kirschbaum followed up asking for a status update. On May 5, the Exim maintainers acknowledged the report and confirmed a private fix was already in progress. On May 7, coordinated disclosure with distribution maintainers began.
On May 12, 2026, CVE-2026-45185 was published and Exim 4.99.3 shipped with the fix, both on the same day. XBOW published the Dead.Letter writeup with full technical details the same day. A public detection script appeared on GitHub shortly after.
Eleven days from initial report to public patch is fast for an open source project with a small maintainer team. It was possible because XBOW delivered a detailed, precise report from the start — the Exim team could start writing the fix immediately rather than spending time reproducing the issue.
WHY IT MATTERS
Exim isn’t niche software. It handles a significant share of internet email traffic, and most Debian/Ubuntu installations use GnuTLS. Mail servers are exposed to the internet by design and accept connections from any client — the only barrier between an attacker and this bug is the Exim version and the TLS library it was built against.
The scale of potentially vulnerable systems makes it worse: many hosting providers and VPS operators ship images with Exim pre-installed. Sysadmins who’ve never deliberately configured an MTA may not know Exim is running on their server at all — let alone which TLS library the binary was built with.
And then there’s the historical context: a use-after-free in BDAT already existed in Exim back in 2017 — CVE-2017-16943, also CVSS 9.8. The same component, the same class of bug, nine years apart. This isn’t carelessness — it’s a signal that the boundary between the TLS layer and the SMTP handler in Exim is structurally complex and hard to audit. The good news is that XBOW found this bug with tooling, without spending months in the codebase. The bad news is that the same tools are available to attackers.
UPDATE
The official Exim advisory is unambiguous: there is no mitigation other than upgrading. No configuration flags, no “disable CHUNKING and wait” — just the patch.
Start by checking your Exim version and TLS library. The exim -bV command prints build details — version, compile date, and a list of supported features including the TLS library:
exim -bV | grep -E "version|TLS"
Look for the version line and the support list. If you see GnuTLS and a version below 4.99.3 — the server is vulnerable and needs updating immediately. If you see OpenSSL instead of GnuTLS — this build isn’t affected.
On Debian/Ubuntu, update through apt. First check whether the repository already has the fixed version:
apt-cache policy exim4
Check the Candidate: line. If it shows 4.99.3 or later, update:
apt update && apt install exim4
If the repository hasn’t caught up yet, download directly from the official Exim FTP — both addresses are listed in the official advisory: ftp.exim.org/pub/exim/exim4/ and code.exim.org/exim/exim/releases. To download and unpack:
wget https://ftp.exim.org/pub/exim/exim4/exim-4.99.3.tar.xz
tar xf exim-4.99.3.tar.xz
Building from source requires the right dependencies and a Local/Makefile configuration file — for most production servers it’s easier to wait for the package repository to update than to build manually. If waiting isn’t an option, build with the same parameters as your current installation (visible in exim -bV output). After updating, verify the version:
exim -bV | grep version
The output should show 4.99.3. For checking multiple servers quickly, there’s a detection script by liamromanis101 on GitHub (Dead.Letter-CVE-2026-45185) — it checks the version, TLS backend, and configuration. It needs read access to the Exim config file, so run it as root or a user in the Debian-exim group.
CONCLUSIONS
Dead.Letter: one byte into freed memory, CVSS 9.8, in a component that accepts connections from the entire internet without authentication. The official Exim advisory leaves no room for interpretation — there’s no mitigation, only the upgrade to 4.99.3.
Check right now: exim -bV | grep -E "version|TLS". If you see GnuTLS and a version below 4.99.3 — update. If the repository hasn’t caught up — download directly from ftp.exim.org. This isn’t something to schedule for next sprint.
