Security News

Cybersecurity news aggregator

🔓
CRITICAL Vulnerabilities Reddit r/netsec

Dead.Letter (CVE-2026-45185) How XBOW found an unauthenticated RCE on Exim

CVE-2026-45185 is a critical unauthenticated remote code execution vulnerability in Exim, triggered by a use-after-free during TLS shutdown when using the GnuTLS library, which allows a single-byte write into freed memory to corrupt allocator metadata. The article does not provide a CVSS score, specific affected version ranges, a fixed version, or a recommended workaround.
Read Full Article →

May 12, 2026 Security Research Federico Kirschbaum Andres Luksenberg Back to Blog Dead.Letter (CVE-2026-45185) How XBOW found an unauthenticated RCE on Exim XBOW discovered CVE-2026-45185, a critical unauthenticated RCE in Exim, and used the disclosure window to test how far human and autonomous exploit development could go. CVE-2026-45185 .Intro Dear reader, What follows is, before anything else, a story. One of those old, well-worn ones. A story of encounters and misencounters, of broken hearts and quiet betrayals, of loves once thought to be forever turning out to be something else entirely. Told, this time, in a setting where stories of that shape are not usually told. These pages are the by-product of the early days of testing a product we are building. A product focused on finding and detecting vulnerabilities in native code. So what you are about to read is two things at once. It is the technical account of a vulnerability of worldwide reach that we found and reported and also it is, more quietly, the account of how I tried to make peace with the new shape of the world we are now living in. I have spent almost ten years writing exploits professionally, and twenty in security altogether. In recent years large language models have arrived to shift the paradigm, and until now I had kept myself on the sidelines whenever it came to using them for writing exploits. This is the first time I have set that watch down and let one of these models into the place where, until now, only my own hands had ever been. Furthermore, in my entire career I had never once read a line of Exim's source. I remembered, vaguely, a Qualys write-up (https://www.qualys.com/2021/05/04/21nails/21nails.txt) from years ago that had blown my mind at that time, but I had never sat down with the code itself. I hope what follows finds two kinds of readers: the ones who came for the technical depth, and the ones who came for the story. I would be glad if it found both at once. The vulnerability The bug is a use-after-free triggered when a TLS connection is handled by GnuTLS (the default TLS library on many Debian-based distributions, including Ubuntu). During TLS shutdown, Exim frees its TLS transfer buffer — but a nested BDAT receive wrapper can still process incoming bytes and end up calling ungetc(), which writes a single character (\n) into the freed region. That one-byte write lands on Exim's allocator metadata, corrupting the allocator's internal shape; the exploit then leverages that corruption to gain further primitives. What matters here is that triggering this bug requires almost no special configuration on the server. That, more than the technical shape of the corruption itself, is what makes it one of the highest-caliber bugs discovered in Exim to date. The write primitive might seem deceptively weak at first glance: it puts a single newline character into a freed memory region. But as the rest of this post will show, that one byte is enough to escalate all the way to remote code execution. What follows is a walk through the technical details of how Exim works, and where the bug lives inside that machinery. If you came only for the story, feel free to jump ahead to [Setting the challenge]. Note: all code in this post is from Exim 4.97 as it ships in the Debian-based, including Ubuntu 24.04 LTS, default installations. Exim Basics When a client issues STARTTLS over a plaintext SMTP session, Exim's command dispatcher runs the following handler: #File: src/smtp_in.c int smtp_setup_msg ( void ) { // ... switch ( smtp_read_command (...)) { // ... case STARTTLS_CMD: HAD (SCH_STARTTLS); if (!fl.tls_advertised) { done = synprot_error (L_smtp_protocol_error, 503 , NULL , US "STARTTLS command used when not advertised" ); break ; } /* Apply an ACL check if one is defined */ if ( acl_smtp_starttls && (rc = acl_check (ACL_WHERE_STARTTLS, NULL , acl_smtp_starttls, &user_msg, &log_msg)) != OK) { done = smtp_handle_acl_fail (ACL_WHERE_STARTTLS, rc, user_msg, log_msg); break ; } // ... s = NULL ; if ((rc = tls_server_start (&s)) == OK) //[1] { // ... DEBUG (D_tls) debug_printf ( "TLS active\n" ); break ; /* Successful STARTTLS */ } // ... } // ... } [1] calls tls_server_start(), which ends up calling tls_init(), then allocates a fresh GnuTLS server session #File: src/tls-gnu.c static int tls_init ( const host_item *host, smtp_transport_options_block * ob, const uschar * require_ciphers, exim_gnutls_state_st **caller_state, tls_support * tlsp, uschar ** errstr) { //... state = &state_server; state->tlsp = tlsp; DEBUG (D_tls) debug_printf ( "initialising GnuTLS server session\n" ); rc = gnutls_init (&state->session, GNUTLS_SERVER); //... } state->session is a gnutls_session_t , which is the GnuTLS handle that owns every piece of cryptographic state for this TLS connection: the negotiated cipher suite, the record-layer keys, the read and write sequence numbers, the ALPN selection, etc., but the important thing is that it also handles transport call...

Share this article