Skip to content

GHSA-4ffr-jq9g-5ffx CVE-2026-110869

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

High npm
Severity
8.7 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.37%
Chance of exploitation in the next 30 days. Higher than 30.54% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-1333 Inefficient Regular Expression Complexity
Fix
10.0.13
Upgrade nodemailer to this version or later

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.

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.

Runtime:      Node.js >= 20
Dependencies: none
Invocation:   node poc.js
'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`);
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 targeted quadratic reassembly of long multiline replies inside _onData and left the AUTH regex evaluation in _actionEHLO untouched. maxResponseSize does not prevent this issue: at 4,000 whitespace characters, roughly 0.4 percent of the default 1 MB cap, a single continuation line already blocks each of the four regexes for tens of seconds. No status-code or length gate rejects the payload before the regex engine enters the ambiguous alternation, and Node.js's single-threaded event loop cannot service the socket timeout while a synchronous test() call is running. Two other recent linear-time fixes in this project (the addressparser fallback for GHSA-v53p-9fqp-m79j and the DKIM header unfolding for GHSA-39m8-27wv-hr27) address entirely different modules and inputs and do not touch the AUTH mechanism regexes.

Affected Versions

  • Ecosystem: npm
  • Package: nodemailer
  • Confirmed affected range: >= 3.0.0, <= 10.0.12
  • Latest release checked: 10.0.12 (npm registry)
  • Fix status: 10.0.13

The four AUTH mechanism regexes were introduced when the SMTP client was bundled into nodemailer at 3.0.0 (previously a separate smtp-connection package). The vulnerable alternation (?:(\s+|=)[^\n]*\s+|\s+|=) is present, unchanged, in every published release from 3.0.0 (lib/smtp-connection/index.js) through 10.0.12 (dist/esm/smtp-connection/index.js and dist/cjs/smtp-connection/index.js). A later refactor added a leading [ -] character class but did not alter the ambiguous inner alternation. No release, and no development branch inspected at the current HEAD (30e4cfd), contains a fix for this pattern.

Suggested Fix

Replace the four backtracking regexes with a non-ambiguous parse of the AUTH extension. The invariant the fix must enforce is that mechanism detection runs in time linear in the response length regardless of whitespace shape. A minimal approach: match the AUTH extension value with a single non-backtracking pattern (for example, /[ -]AUTH(?:\s+|=)([^\r\n]*)/i) and then check for each mechanism name against the whitespace-split tokens with a Set lookup or includes. The fix must cover both continuation forms (250-AUTH ... and 250 AUTH ...) and both delimiters (space and =), and must handle the "AUTH advertised with no mechanisms" case without walking the whitespace run. As an interim mitigation, callers who never connect to an untrusted SMTP peer can restrict SMTP endpoints to a known allowlist; this does not help clients that must interoperate with arbitrary user-configured servers or with any cleartext SMTP path exposed to a network-adjacent attacker before STARTTLS.


Reported by zx (GitHub: @manus-pi).

Affected versions

npm

PackageVulnerableFixed in
nodemailer
>= 3.0.0, < 10.0.13
10.0.13

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

nodemailer npm → 10.0.13

npm
npm install nodemailer@10.0.13
yarn
yarn add nodemailer@10.0.13
pnpm
pnpm add nodemailer@10.0.13

Severity breakdown

CVSS 4.0 8.7

CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
None
Integrity (vulnerable system)
None
Availability (vulnerable system)
High
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None
Exploit maturity
Not defined
Scope
X

CVSS 3.1 5.9

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Weakness

CWE-1333: Inefficient Regular Expression Complexity

The product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential.

How to prevent it
  • Architecture and Design: Use regular expressions that do not support backtracking, e.g. by removing nested quantifiers.
  • System Configuration: Set backtracking limits in the configuration of the regular expression implementation, such as PHP's pcre.backtrack_limit. Also consider limits on execution time for the process.
  • Implementation: Do not use regular expressions with untrusted input. If regular expressions must be used, avoid using backtracking in the expression.
  • Implementation: Limit the length of the input that the regular expression will process.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-8c8m-p3m8-cjg8
Nodemailer: A nested comment ends early in addressparser, releasing the rest of the comment as a recipient
Medium Also affects nodemailer
GHSA-m943-m63m-w62w
Nodemailer: A ";" inside a quoted string or comment ends an address group early in addressparser, turning display text into a recipient
Medium Also affects nodemailer
GHSA-85x2-q946-cp5h
Nodemailer: A ">" inside a quoted local part ends an angle-addr early in addressparser, delivering to a domain the header does not name
Medium Also affects nodemailer
GHSA-4g23-2xm8-66gc
Nodemailer: Quadratic (O(n^2)) SMTP client reply parsing lets a malicious server or MITM cause a pre-auth remote denial of service
High Also affects nodemailer
GHSA-39m8-27wv-hr27
Nodemailer: Quadratic (O(n^2)) DKIM header unfolding lets a large folded header cause remote denial of service during signing
High Also affects nodemailer
GHSA-g73g-hqqh-jr95
Nodemailer: RFC 5322 comment inside an angle-bracket address is never stripped
Medium Also affects nodemailer
GHSA-8vvx-rff5-p5rq
Nodemailer: Nested structured recipient arrays bypass the parser depth limit and cause stack exhaustion DoS
High Also affects nodemailer
GHSA-6vj9-mwq6-2f5v
Nodemailer: Process-global DNS cache reuses TLS `servername` across transports, enabling cross-tenant SMTP credential disclosure
Medium Also affects nodemailer
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor