CVE-2023-36632 - Understanding the Controversy Around Python’s `email.utils.parseaddr` “RecursionError”
In June 2023, a security issue was filed under the label CVE-2023-36632 affecting Python’s email.utils.parseaddr function (up to version 3.11.4). If you feed this function a specially crafted input, you can crash your application with a RecursionError: maximum recursion depth exceeded while calling a Python object.
But here’s the catch: The Python developers disagree that this is an actual vulnerability. This write-up will break down why, how the issue works, and what you should do instead.
The Core of the Issue
The legacy parseaddr function is meant to split a string into a name and email address. For example, "John Doe <john@example.com>" becomes ("John Doe", "john@example.com").
However, if you pass it a weirdly nested or malformed address (especially one with brackets that never close), parseaddr will endlessly call itself and eventually hit Python's recursion limit — crashing with a RecursionError.
Real-World Exploit Scenario
If your application lets users input email addresses and you use parseaddr to process them without validation, an attacker could send a payload like:
(((((((((((...
or
"(((A <a@b.com"
— hundreds or thousands of brackets. This would make parseaddr go down the rabbit hole, blowing up the call stack.
Here’s some simple code showing this in Python 3.11.4 or earlier
from email.utils import parseaddr
# Create an input with a huge number of nested opening parentheses
evil_input = "(" * 200 + "A <a@b.com"
try:
name, email = parseaddr(evil_input)
except RecursionError as e:
print("Crash: RecursionError occurred")
Expected output
Crash: RecursionError occurred
If evil_input is much longer, even with stack size increases the process can become unresponsive.
Is This Actually a Vulnerability?
Python’s position: No, this is not a bug or a vulnerability.
- The Python email package documentation *clearly* says parseaddr is a “Legacy API.”
- Robust applications should use email.parser.Parser or email.parser.BytesParser for better protection and standards-compliant parsing.
- The Python devs expect exceptions when you go beyond reasonable input sizes. They specifically designed it to die with a RecursionError for deeply nested garbage input.
Security researchers’ position: From a security viewpoint, anything that can be crashed by bad user data should be treated with caution, especially legacy software where such exceptions can bubble up and crash daemons, bots, or microservices.
References
- Python Official Docs: parseaddr
- NIST CVE detail page
- GitHub Python Bug Tracker Discussion
- Email package improvement suggestion
Validate input length. Set a max allowed input size before sending it to parsing functions.
3. Handle exceptions everywhere. Even if you sanitize, always program defensively to catch RecursionError and log unexpected behavior.
Always validate and sanitize user input before parsing.
If you’re maintaining code using parseaddr, now’s the time to upgrade – or at least add strong input checks.
*Written exclusively for clarity. Content based on public records and direct review of the Python project.*
Timeline
Published on: 06/25/2023 18:15:00 UTC
Last modified on: 07/06/2023 16:00:00 UTC