Skip to content

ContentType.Boundary accepts CRLF allowing MIME structure injection via MimeWriter #131820

Open

Description

@bolverk

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:

    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:

    _boundaryBytes = Encoding.ASCII.GetBytes(boundary);
  3. MimeWriter.CheckBoundary() (MimeWriter.cs:70-79) — Writes raw boundary bytes into MIME body:

    _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:

    MimeWriter mimeWriter = new MimeWriter(outputStream, ContentType.Boundary!);

Repro

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<html><body><h1>INJECTED</h1></body></html>";
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<html><body><h1>INJECTED</h1></body></html>"

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

<html><body><h1>INJECTED</h1></body></html>  ← 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:

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

  • System.Net.Mail Validation improvements #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.

Activity

  1. dotnet-policy-service commented on Aug 4, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @karelz, @dotnet/ncl
    See info in area-owners.md if you want to be subscribed.

  2. dotnet-policy-service commented on Aug 4, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @karelz, @dotnet/ncl
    See info in area-owners.md if you want to be subscribed.

  3. GrabYourPitchforks commented on Aug 5, 2026

    @GrabYourPitchforks
    Member

    Security review: not a vulnerability. The Boundary property is meant to dictate framing of the data structure, which makes it a control-plane argument rather than a data-plane argument. No trust boundary has been crossed.

    EG has discretion as to whether they want to consider this a non-security reliability bug.

  4. removed
    untriagedNew issue has not been triaged by the area owner
    on Sep 8, 2026
  5. added this to the Future milestone on Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions