HACKERONE

curl: SMTP CRLF Command Injection in CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT_H1:3414088

Description

libcurl's SMTP implementation accepts CR (`\r`) and LF (`\n`) bytes in mailbox address inputs without validation. These control characters are inserted directly into SMTP commands, allowing attackers to inject arbitrary SMTP protocol commands. This enables envelope manipulation, adding unauthorized recipients, and potentially bypassing application-level email controls.

### Steps To Reproduce

**Environment:**
- Target: curl/libcurl source code (https://github.com/curl/curl)
- Tested versions:
- curl 8.17.0 (latest official release) ✅ VULNERABLE
- curl 8.12.0-DEV (commit 58023ba52273b05deb36ec1d395df18ba29b3bde) ✅ VULNERABLE
- Build: Standard release build
- Date tested: 2025-11-06

**Prerequisites:**
1. Build curl from source (commit 58023ba52273b05deb36ec1d395df18ba29b3bde)
2. Download the attached proof-of-concept script: `poc_smtp_crlf_injection.sh`

**Steps:**

1. Make the script executable:
```bash
chmod +x poc_smtp_crlf_injection.sh
```

2. Run the proof-of-concept with the path to your curl binary:
```bash
./poc_smtp_crlf_injection.sh /path/to/curl/src/curl
```

The script will:
- Start an SMTP server that logs all received commands
- Execute curl with a CRLF-injected mailbox address
- Display clear before/after comparison
- Highlight the injected commands

3. Observe the output showing the split SMTP commands

**What should happen:**
curl should reject the mailbox address containing control characters with an error like "invalid characters in mailbox address" or "control characters not allowed", similar to how it rejects null bytes in HTTP headers.

**What actually happens:**
curl accepts the CRLF characters and sends them as part of the SMTP command, resulting in protocol-level command injection:

```
> MAIL FROM:<[email protected]
> RCPT TO:<[email protected]> SIZE=... ← INJECTED COMMAND
> RCPT TO:<[email protected]> ← LEGITIMATE RECIPIENT
```

The MAIL FROM command is split into two separate lines, with "RCPT TO:<[email protected]>" being injected as a separate SMTP command. The email is now sent to both the legitimate recipient AND the attacker's address.

### Evidence

**Attached file demonstrating the vulnerability:**

**poc_smtp_crlf_injection.sh** - Complete, self-contained proof-of-concept script that:
- Starts a local SMTP server to capture commands
- Runs curl with CRLF-injected mailbox addresses
- Displays clear evidence of command injection
- Requires only Python 3 and bash (no other dependencies)
- Takes ~30 seconds to run
- Exit code 1 = vulnerable, 0 = patched

**Sample output from the PoC script:**

```
══════════════════════════════════════════════════════════════════
VULNERABILITY CONFIRMED
══════════════════════════════════════════════════════════════════

✗ VULNERABLE: CRLF injection successful!

SMTP Commands Sent by curl:
──────────────────────────────────────────────────────────────────
> EHLO smtp_test_body.txt
> MAIL FROM:<[email protected]
> RCPT TO:<[email protected]> SIZE=59 ← INJECTED!
> RCPT TO:<[email protected]>
> DATA

Result: Email sent to BOTH recipients:
[email protected] (legitimate)
[email protected] (INJECTED)
```

The script output clearly shows:
1. The MAIL FROM command split into two lines
2. The injected RCPT TO command on a separate line
3. Both recipients being accepted by the SMTP server
4. Confirmation that the email would be sent to both addresses

**Code analysis showing the vulnerability:**

File: `lib/smtp.c`, lines 838-846
```c
result = Curl_pp_sendf(data, &smtpc->pp,
"MAIL FROM:%s%s%s%s%s%s",
from, /* Mandatory - NO VALIDATION */
auth ? " AUTH=" : "",
auth ? auth : "",
size ? " SIZE=" : "",
size ? size : "",
utf8 ? " SMTPUTF8" : "");
```

File: `lib/smtp.c`, lines 1875-1921 (`smtp_parse_address`)
- Function performs basic parsing (strips `<>`, finds `@`)
- No validation for control characters (CR, LF, NUL)
- Strings are passed directly to command construction

**Evidence that this is NOT by-design:**

curl already rejects control characters in similar contexts. From `lib/cookie.c`, lines 436-446:

```c
static bool invalid_octets(const char *ptr) {
const unsigned char *p = (const unsigned char *)ptr;
/* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
while(*p) {
if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
return TRUE;
p++;
}
return FALSE;
}
```

This function explicitly rejects CR (0x0D) and LF (0x0A) in cookie values. The same validation should apply to SMTP mailbox addresses but is missing.

## Impact

An attacker who controls email address inputs can inject SMTP commands by adding CRLF characters. This lets them add extra recipients to emails without the application knowing. For example, a password reset email meant for one person gets secretly copied to the attacker's email address.

**Real attack scenario:** A web app sends password reset emails using curl. An attacker registers username `victim\r\nRCPT TO:<[email protected]>` on the platform. When the app constructs the email address and passes it to curl, curl injects the extra recipient command. The attacker receives a copy of the password reset link meant for the victim.

**Why this matters:**
- Many apps build email addresses from usernames + domain without checking for control characters
- Apps expect the library to handle protocol-level validation (like curl does for cookies and HTTP headers)
- Attackers can steal sensitive emails: password resets, verification codes, confidential reports
- The vulnerability affects all apps using `CURLOPT_MAIL_FROM`, `CURLOPT_MAIL_RCPT`, or `CURLOPT_MAIL_AUTH` with any user input
Visit Original Source

Basic Information

ID H1:3414088
Published Nov 6, 2025 at 12:07
Modified Nov 10, 2025 at 10:39

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.