Repository navigation
EmailMessage objects break when folding malformed header #132105
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Apr 5, 2025 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Apr 5, 2025 Two further notes:
- Newlines within the encoded header also fold with added newlines, allowing any number of extra headers to be injected, and
- 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.
I’m interested in working on this issue and would appreciate any advice or recommendations you might have to get started.
@rijo7 a good starting point is comparing the behaviour of different Python sub-versions. Some have the vulnerability, some don't.
@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).
@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-Versionand the rest of the header block stay intact. I checked the same round-trip under the default, SMTP, SMTPUTF8, HTTP and compat32 policies (bothas_string()andas_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, withverify_generated_headersas 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, andtest_verify_generated_headersinLib/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@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
\nin an encoded-word.)Reacted by tonghuaroot (童话)
Bug report
Bug description:
In certain cases, with
EmailMessageobjects, 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.xand3.9.xare affected by the bug.The specific malformation is where:
\nAn example of this:
Here is a test script which reproduces the problem on many/most recent Python versions:
Python 3.11.11 flattens the message correctly. Here is an excerpt in/around the
Subject:header: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:
Note the blank line after
Subject:.When messages folded and delivered with this breakage are delivered, MTAs will often add
Content-Type: text/plainbecause 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:
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
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