MIME multipart abuse

A multipart email is a structured container: a top-level Content-Type: multipart/...; boundary="X" header declares a delimiter string, and each part is introduced by --X followed by its own headers and content, with --X-- closing the set. When an application lets untrusted input reach the boundary token or a part's headers, the attacker can forge part separators, add their own parts, or make two parsers disagree about where parts begin and end.

Where input reaches the structure

Two sinks dominate. First, header injection (CRLF into a header field) that reaches the Content-Type line lets the attacker declare or redefine the boundary. Second, templates that interpolate user data into a hand-built multipart body place attacker bytes directly between part separators. In both cases the attacker needs the boundary string, which is often predictable: static in the source, derived from a timestamp, or echoed in an earlier response.

Adding a part or attachment

Once the boundary is known, injecting a CRLF sequence plus a fresh --boundary block appends a part the application never intended. Delivering an HTML part:

x@evil.test%0d%0aContent-Type: multipart/mixed; boundary="AaB03x"%0d%0a%0d%0a--AaB03x%0d%0aContent-Type: text/html%0d%0a%0d%0a<h1>Phish</h1>%0d%0a--AaB03x--

The same shape carries an attachment by setting Content-Disposition: attachment; filename="invoice.html" and a base64 body, turning a plain notification mailer into a malware or phishing-document delivery channel sent from the trusted domain.

Smuggling an alternative body past a filter

multipart/alternative tells the client to render the last part it understands, usually the HTML one, while content scanners frequently inspect only the first text/plain part. The attacker ships a clean plaintext part and a malicious HTML part under the same boundary:

--AaB03x
Content-Type: text/plain

Your account summary is attached.
--AaB03x
Content-Type: text/html

<a href="https://evil.test">Review your account</a>
--AaB03x--

The filter clears the innocuous text; the victim's client shows the HTML. The reader and the scanner never see the same message.

Parser desynchronization

MTAs, gateways, and clients each implement boundary matching slightly differently: handling of leading/trailing whitespace on the delimiter, of a near-miss boundary (--AaB03x with a trailing space), of a duplicate Content-Type header, or of a boundary that also appears inside a part's content. Injecting a boundary that one hop treats as a separator and the next treats as literal text makes the gateway and the client parse different structures from the same bytes, so a part visible to one is swallowed by the other. Colliding the declared boundary with a string that appears naturally in the body is a reliable way to split the message at an unexpected point.

Making it land

  • The boundary string must be known or controllable; harvest it from source, leaked headers, or a predictable generator before building parts.
  • Part separators and part headers both need real CRLFs; on the wire from a web request that is %0d%0a, and the blank line before a part's content is a double %0d%0a%0d%0a.
  • A part must end with CRLF immediately before the next --boundary, and the final delimiter needs its trailing --.

References

Cookie Consent

We use cookies to enhance your experience. Learn more