GHSA-4ffr-jq9g-5ffx
Nodemailer: Quadratic backtracking in SMTP AUTH mechanism detection regexes allows pre-auth remote denial of service
- Risk score
- 45/100
- CVSS
- 5.9 v3.1
- Published
- 27 Oct 2026
Open in the live forecast Today's forecast
What to know
- What it is
- 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 mali…
- Affected
- nodemailer (npm) >= 3.0.0, <= 10.0.12
- Fix
- Upgrade nodemailer to 10.0.13 or later.
- Exploitation
- Public proof of concept
Story map
At a glance
nodemailer: Amber, patch this week. nodemailer/lib/addressparser parses one shape of address in O(n^2) time. A single ~640 KB address value blocks the Node.js event loop for roughly 7 seconds. And it is reachable without auth: mailparser feeds inbound email headers straight into this parser, so one crafted email is enough to stall a service that parses …
The flaws
- GHSA-4ffr-jq9g-5ffx: Nodemailer: Quadratic backtracking in SMTP AUTH mechanism detection regexes allows pre-auth remote denial of service (CVSS 5.9)
- CVE-2026-90776: Nodemailer addressparser: O(n^2) on comment-joined addresses enables a remote DoS (reachable via mailparser) (CVSS 7.5 · EPSS 1%)
- CVE-2026-110871: Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing (CVSS 6.5 · EPSS 0%)
- GHSA-v53p-9fqp-m79j: Nodemailer: Quadratic backtracking in the addressparser free-text fallback allows remote denial of service (CVSS 7.5)
The fix
Upgrade nodemailer: 10.0.10 or later on your release line
Who is exposed: nodemailer (npm) >= 9.1.0, <= 10.0.4
Where it stands
- Public proof of concept
Connects to
- Same weakness: CWE-1333 (CWE-1333). 48 other warnings in 30 days
Why it is Yellow
- CVSS 5.9 medium
- Public exploit or proof of concept referenced
- On your watchlist: npm
Affected packages
| Package | Ecosystem | Vulnerable | Fixed in |
|---|---|---|---|
| nodemailer | npm | >= 3.0.0, <= 10.0.12 | 10.0.13 |
References
- https://github.com/advisories/GHSA-4ffr-jq9g-5ffx
- https://github.com/nodemailer/nodemailer/security/advisories/GHSA-4ffr-jq9g-5ffx
- https://github.com/nodemailer/nodemailer/commit/b5a896fcb02df1628329d7cd665ca08490b14b56
- https://github.com/nodemailer/nodemailer/releases/tag/v10.0.13
- https://github.com/advisories/GHSA-4ffr-jq9g-5ffx
Original description
## 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<whitespace>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 zero time. ## Attack Chain 1. **Exposure:** The victim application, or any dependent that uses `nodemailer` to send mail, opens a TCP or TLS connection to an SMTP host the attacker controls. This happens automatically for multi-tenant mailers whose tenants configure their own outbound SMTP server, and for any client whose plaintext SMTP session can be intercepted by a network-adjacent attacker before STARTTLS negotiation. 2. **Control:** The attacker fully controls the bytes returned in response to `EHLO`. In particular, the attacker chooses the length and content of every `250-` continuation line up to the client's 1 MB response cap. 3. **Path:** Socket bytes are reassembled by `_onData`, decoded by `_processResponse`, and dispatched to `_actionEHLO`. On the first EHLO after the greeting (malicious server with no STARTTLS), on the second EHLO after a successful STARTTLS upgrade, or on the first EHLO when `ignoreTLS` is set, control reaches the AUTH detection block near the end of `_actionEHLO`. 4. **Guard:** The only bounds on this path are `maxResponseSize` (1 MB by default) and the 421 or non-2xx status-code check. Neither prevents O(n^2) backtracking: 4,000 whitespace characters already block each regex for tens of seconds, well below the 1 MB cap. No length, shape, or sanitization check runs before the regexes. 5. **Primitive:** Four `RegExp.prototype.test` calls execute against the crafted line, each entering catastrophic backtracking because `[^\n]*` and `\s+` share the whitespace alphabet and the mechanism literal never matches. 6. **Result:** The Node.js event loop is blocked for the duration of the four regex evaluations. All other work in the process, including timers, unrelated requests, keep-alive traffic, and the SMTP socket timeout itself, is suspended until the regexes return. ## Bypass Evidence The only response-side protection in this code path is `maxResponseSize`, introduced by the fix for GHSA-4g23-2xm8-66gc (commit `f2d82fa`). That fix targete
Last activity 27 Oct 2026. Patch Forecast updates every hour.