dotnet / dotnet/runtime

ContentType.Boundary accepts CRLF allowing MIME structure injection via MimeWriter

Open Beginner friendly
#131,820 3 comments 0 reactions 0 assignees View on GitHub
area-System.Net
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Description

`ContentType.Boundary` accepts CRLF characters (`\r\n`) without validation. When a user-controlled boundary containing CRLF is used, `MimeWriter` writes the **raw** boundary bytes into the MIME body via `CheckBoundary()`, while the Content-Type **header** encodes the boundary through `GetTokenOrQuotedString()`. This mismatch allows injection of arbitrary MIME headers and content into the message body.

## Vulnerable Code Path

1. **`ContentType.Boundary` setter** (`ContentType.cs:55-68`) — No CRLF validation:
```csharp
public string? Boundary
{
set
{
_parameters ??= new StringDictionary();
_parameters["boundary"] = value;
_isChanged = true;
}
}
```

2. **`MimeWriter` constructor** (`MimeWriter.cs:25`) — Converts boundary to raw ASCII bytes, preserving CRLF:
```csharp
_boundaryBytes = Encoding.ASCII.GetBytes(boundary);
```

3. **`MimeWriter.CheckBoundary()`** (`MimeWriter.cs:70-79`) — Writes raw boundary bytes into MIME body:
```csharp
_bufferBuilder.Append("\r\n--"u8);
_bufferBuilder.Append(_boundaryBytes); // Contains injected CRLF!
_bufferBuilder.Append("\r\n"u8);
```

4. **`MimeMultiPart.SendAsync()`** (`MimeMultiPart.cs:48`) — Passes raw `ContentType.Boundary` to MimeWriter:
```csharp
MimeWriter mimeWriter = new MimeWriter(outputStream, ContentType.Boundary!);
```

## Repro

```csharp
using System;
using System.Net.Mail;
using System.Net.Mime;
using System.Text;

var msg = new MailMessage(new MailAddress("from@example.com"), new MailAddress("to@example.com"));

var view = AlternateView.CreateAlternateViewFromString("This is the legitimate content.", Encoding.UTF8);
view.ContentType = new ContentType("text", "plain");
view.ContentType.Boundary = "legit\r\nContent-Type: text/html\r\n\r\n

INJECTED

";
msg.AlternateViews.Add(view);

msg.Subject = "Test";
msg.Body = "";

// When serialized (e.g. via SmtpClient or pickup directory), the MIME output contains injected headers.
```

## Resulting MIME Output (reconstructed)

```
Content-Type: multipart/alternative; boundary="legit\r\nContent-Type: text/html\r\n\r\n

INJECTED

"

--legit
Content-Type: text/html ← INJECTED HEADER

INJECTED

← INJECTED CONTENT

Content-Type: text/plain ← Start of legitimate boundary
This is the legitimate content.
--legit--
```

The Content-Type **header** correctly encodes the boundary (CRLF is escaped by `GetTokenOrQuotedString`), but the MIME **body** uses the raw boundary bytes, creating the mismatch that enables injection.

## Impact

- **Content injection**: Arbitrary HTML/text content injected into email body
- **Header injection**: Arbitrary MIME headers (Content-Type, Content-Disposition, etc.) injected into the MIME body structure
- **Potential XSS**: If injected HTML is rendered by email client
- **Boundary confusion**: Legitimate MIME parts may be misinterpreted or skipped

## Suggested Fix

Add CRLF validation to `ContentType.Boundary` setter, rejecting values containing `\r` or `\n`:

```csharp
public string? Boundary
{
set
{
if (value != null && value.AsSpan().ContainsAny(r, n))
{
throw new ArgumentException("Boundary must not contain CR or LF characters.");
}
_parameters ??= new StringDictionary();
_parameters["boundary"] = value;
_isChanged = true;
}
}
```

## Related

- **#128981**: Validates `ContentType.Name` and `ContentDisposition.FileName` against header injection (different attack vector — parameter injection via crafted RFC 2047 encoded-words, not CRLF in boundary)
- **CVE-2026-32178**: CRLF injection in `MailAddress` display names (patched — address parser, not boundary)
- **CVE-2026-50659**: SMTP dot-stuffing smuggling in `EightBitStream.EncodeLines` (patched — different attack surface)

## Affected Versions

All supported .NET versions (8, 9, 10) — the `ContentType.Boundary` setter has never validated for CRLF.

Contributor guide

Open the contributing guide

Research direction

Start with the ContentType.Boundary setter in ContentType.cs and trace how its value reaches the MimeWriter constructor and CheckBoundary(), via MimeMultiPart.SendAsync(). Add validation so CR and LF cannot reach the raw MIME boundary bytes, then verify that such boundary values are rejected and normal MIME serialization remains unaffected.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.