GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — EmailMessage objects break when folding malformed header · Issue #132105 · python/cpython

EmailMessage objects break when folding malformed header #132105

New issue
Copy link
New issue
Copy link
Closed as duplicate
Closed as duplicate
EmailMessage objects break when folding malformed header#132105
Copy link
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-emailtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@davidmcnabnz
davidmcnabnz
opened on Apr 5, 2025
Issue body actions

Bug report

Bug description:

In certain cases, with EmailMessage objects, encoded headers can fold with double line endings, causing breakage of flattened message objects, making messages display improperly in email clients and rendering attachments inaccessible.

Python3.11.11 does not exhibit this behaviour. But numerous other Python versions eg 3.11.9, 3.12.x, 3.10.x and 3.9.x are affected by the bug.

The specific malformation is where:

  • a header is long
  • a header is presented with its value presented as RFC 2047 UTF8 base64
  • when decoded, the header's payload ends with a newline \n

An example of this:

Subject: =?utf-8?B?Vm9pY2Vib3ggRmlybWE6IFRlc3QtTmFjaHJpY2h0IHVtIDEwOjAzOjM1IDA0LjA0LjI1IC0gQUdGRU8gRVMgNTIyIElUIHVwIC0gc3RhaXBzbmV0Cg==?=

Here is a test script which reproduces the problem on many/most recent Python versions:

#!/usr/bin/env python3
"""
Reproduces a bug with flattening email headers whose encoded payloads end in a newline
"""
import sys
from email import policy
from email.parser import Parser, BytesParser, HeaderParser

sampleRaw = """Date: Fri, 04 Apr 2025 10:03:35 +0200\r
From: [email protected]\r
Subject: =?utf-8?B?Vm9pY2Vib3ggRmlybWE6IFRlc3QtTmFjaHJpY2h0IHVtIDEwOjAzOjM1IDA0LjA0LjI1IC0gQUdGRU8gRVMgNTIyIElUIHVwIC0gc3RhaXBzbmV0Cg==?=\r
MIME-Version: 1.0\r
Content-Type: multipart/mixed;\r
 boundary="235711131719"\r
To: [email protected]\r
\r
This is a multi-part message in MIME format.\r
--235711131719\r
Content-Type: text/plain; charset=UTF-8; format=flowed\r
Content-Transfer-Encoding: 7bit\r
\r
This is readable part of the body\r
--235711131719--\r
\r
"""

messageObj = Parser(policy=policy.default).parsestr(sampleRaw)

print(sys.version)
print("--------")
print(messageObj.as_string())
print("--------")

Python 3.11.11 flattens the message correctly. Here is an excerpt in/around the Subject: header:

From: [email protected]
Subject: Voicebox Firma: Test-Nachricht um 10:03:35 04.04.25 - AGFEO ES 522 IT
 up - =?utf-8?q?staipsnet=0A?=
MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="235711131719"
To: [email protected]

This is a multi-part message in MIME format.

As can be seen here, the header has been re-wrapped to quoted-printable (which is fine), the embedded newline is present in the encoded payload, but when the header is folded out, it has only the one line ending. Great.

But other pythons I've tested with come up with:

From: [email protected]
Subject: Voicebox Firma: Test-Nachricht um 10:03:35 04.04.25 - AGFEO ES 522 IT
 up - staipsnet

MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="235711131719"
To: [email protected]

This is a multi-part message in MIME format.
--235711131719
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit

This is readable part of the body

Note the blank line after Subject:.

When messages folded and delivered with this breakage are delivered, MTAs will often add Content-Type: text/plain because the headers after Subject have been lost into the body.

The recipient of the above message will see this formatted as plain text in their viewing pane:

MIME-Version: 1.0
Content-Type: multipart/mixed;
 boundary="235711131719"
To: [email protected]

This is a multi-part message in MIME format.
--235711131719
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit

This is readable part of the body

If there were any attachments, their encoded representations will appear after that as gibberish strings, and the mail client won't indicate an attachment is present.

This has potentially serious security implications. If an email delivery chain has python stdlib-based mail processing at or near the end, a carefully structured malicious header can

  • evade sanitisation
  • nullify and/or replace headers added upstream in the chain
  • inject malicious extra headers and MIME-encoded context which may cause subsequent handling steps, and/or the final MUA, to be hijacked for exploit attempts.

Workaround for me has been to subclass a policy, and implement a _fold() method to get the parent class' folding, then .rstrip() it, then add "\r\n" to the end.

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Activity

  1. davidmcnabnz
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Apr 5, 2025
  2. skirpichev
    added
    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
    topic-email
    on Apr 5, 2025
  3. davidmcnabnz commented on Apr 6, 2025

    @davidmcnabnz
    davidmcnabnz
    on Apr 6, 2025
    Author
    More actions

    Two further notes:

    1. Newlines within the encoded header also fold with added newlines, allowing any number of extra headers to be injected, and
    2. I am unable to reproduce any of this vulnerable behaviour in a vanilla Python 3.13.2 which I just built from source on Debian.

    So now it's both 3.11.11 and 3.13.2 which wrap illegal line breaks (plus a few chars before/after) in quoted-printable encoding. On the other hand, all other Python versions I've tried have the vulnerability.

  4. rijo7 commented on Apr 6, 2025

    @rijo7
    rijo7
    on Apr 6, 2025
    More actions

    I’m interested in working on this issue and would appreciate any advice or recommendations you might have to get started.

  5. davidmcnabnz commented on Apr 10, 2025

    @davidmcnabnz
    davidmcnabnz
    on Apr 10, 2025
    Author
    More actions

    @rijo7 a good starting point is comparing the behaviour of different Python sub-versions. Some have the vulnerability, some don't.

  6. medmunds commented on May 6, 2025

    @medmunds
    medmunds
    on May 6, 2025
    Contributor
    More actions

    @davidmcnabnz could you identify the specific 3.12.x, 3.10.x and 3.9.x versions where you have seen the bug?

    It's very possible this was fixed by either #122754 (last month, so just now showing up in the latest Python releases) or #122233 (last summer).

  7. tonghuaroot commented on Jun 23, 2026

    @tonghuaroot
    tonghuaroot
    on Jun 23, 2026
    Contributor
    More actions

    @davidmcnabnz @medmunds — on the open question of which fix applies: I can't reproduce this on current main (3.16.0a0), and a bisect pins it down.

    The reporter's exact script now re-encodes the trailing newline inside the encoded-word (…=0A?=) instead of emitting a spurious blank line; MIME-Version and the rest of the header block stay intact. I checked the same round-trip under the default, SMTP, SMTPUTF8, HTTP and compat32 policies (both as_string() and as_bytes()) — no truncation in any.

    Bisecting pins the fix to gh-121650 / PR #122233 (the newline-encoding hardening tracked as CVE-2024-6923): its parent commit still reproduces, the merge itself does not. So of the two candidates floated above, it's #122233 rather than #122754. It forces encoded-word output whenever a header value contains \r/\n, with verify_generated_headers as a backstop (on by default for all policies).

    It first shipped in 3.11.10 (#122608) and 3.13.0, so seeing it resolved on 3.11.11 / 3.13.2 lines up, and 3.11.9 predates the fix. Covered by test_keep_encoded_newlines, test_keep_long_encoded_newlines, and test_verify_generated_headers in Lib/test/test_email/test_generator.py.

    Looks already-fixed to me — likely closeable, unless anyone still sees it on a maintained branch.

    Reacted by Mike Edmunds
  8. medmunds commented on Jun 23, 2026

    @medmunds
    medmunds
    on Jun 23, 2026
    Contributor
    More actions

    @tonghuaroot Thanks for bisecting. Yes, this report is a duplicate of gh-121650 and is already fixed. It can be closed.

    (This example uses base64 rather than quoted-printable encoding in the rfc2047 encoding, but is otherwise exactly the same issue with refolding \n in an encoded-word.)

    Reacted by tonghuaroot (童话)
  9. hugovk
    closed this as a duplicateof email.policy.default - gotcha with re-using parsed headers with embedded newlines #121650 on Oct 29, 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

    Labels

    stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-emailtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.