Issue43123
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2021-02-04 12:48 by martin.ortner, last changed 2022-04-11 14:59 by admin.
Pull Requests | |||
---|---|---|---|
URL | Status | Linked | Edit |
PR 24475 | closed | trrhodes, 2021-02-07 13:19 |
Messages (3) | |||
---|---|---|---|
msg386479 - (view) | Author: Martin Ortner (martin.ortner) | Date: 2021-02-04 12:48 | |
// reported via PSRT email (see timeline) // external reference: https://consensys.net/diligence/vulnerabilities/private/jcchhpke7usq8wo45vloy282phwpd9fj41imumhb8varxahz2bf9afw5mcno84gx/ cve: vendor: python vendorUrl: https://www.python.org/ authors: tintinweb affectedVersions: [at least <= 3.8.3, <=3.7.7, <=2.7.18] vulnClass: CWE-93 # Vulnerability Note ## Summary >Python is a programming language that lets you work more quickly and integrate your systems more effectively. The python `email.mime` package fails to properly encode or reject `CR-LF` control sequences in MIME header values allowing for MIME splitting and header injection attacks. * `MIMEText[headerKey] = headerValue` - `headerValue` accepts `CR-LF` in the value, allowing an attacker in control of part of the header value to perform a MIME splitting attack. * `MIMEText[headerKey] = headerValue` - `headerKey` is not checked for `CR-LF` allowing an attacker in control of part of a header key to inject arbitrary MIME headers. * `MIMEText.add_header(headerKey, headerValue)` - `headerKey` is not checked for `CR-LF` allowing an attacker in control of part of a header key to inject arbitrary MIME headers. ## Details ### MIME-Splitting with `CR-LF` in header value: * Note: `CR-LF` injection in `To` header pushes an invalid header and may force a MIME split (depending on the parsing library) pushing following header values into the body when being parsed with the `email.message_from_string()` method. ```python # Import the email modules we'll need from email.mime.text import MIMEText # Open a plain text file for reading. For this example, assume that # the text file contains only ASCII characters. msg = MIMEText("REAL_MSG_BODY_BEGIN\n...\nREAL_MSG_BODY_END") msg['Subject'] = 'The contents of is this...' msg['To'] = "TO toAddress@oststrom.com\r\nX-SPLIT-MSG-TO-BODY\r\n" msg['From'] = "FROM fromAddress@oststrom.com" msg['MyHEader'] = "hi :: hi" print(msg) print(repr(msg)) print("=========================") import email msg = email.message_from_string(str(msg)) print(msg) print("-> FROM: %s" % msg.get("From")) print("-> TO: %s" % msg["To"]) print("-> MSG: " + repr(msg.get_payload())) ``` Output: * Output before the `===========` is the constructed message * Output after the `===========` is the parsed message * Note: that after parsing the message some headers end up in the body (`from`, `myheader`). Note that `msg[from]` is empty. ``` ⇒ python3 a.py Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: The contents of is this... To: TO toAddress@oststrom.com X-SPLIT-MSG-TO-BODY From: FROM fromAddress@oststrom.com MyHEader: hi :: hi REAL_MSG_BODY_BEGIN ... REAL_MSG_BODY_END <email.mime.text.MIMEText object at 0x108842850> ========================= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: The contents of is this... To: TO toAddress@oststrom.com X-SPLIT-MSG-TO-BODY From: FROM fromAddress@oststrom.com MyHEader: hi :: hi REAL_MSG_BODY_BEGIN ... REAL_MSG_BODY_END -> FROM: None -> TO: TO toAddress@oststrom.com -> MSG: 'X-SPLIT-MSG-TO-BODY\nFrom: FROM fromAddress@oststrom.com\nMyHEader: hi :: hi\n\nREAL_MSG_BODY_BEGIN\n...\nREAL_MSG_BODY_END' ``` ### `CR-LF` injection in header keys. Note: this is unlikely to be exploited, however, there might be scenarios where part of the header key is exposed to user input. A `CR-LF` character in the header key should throw instead. ```python # Import the email modules we'll need from email.mime.text import MIMEText # Open a plain text file for reading. For this example, assume that # the text file contains only ASCII characters. msg = MIMEText("REAL_MSG_BODY_BEGIN\n...\nREAL_MSG_BODY_END") # me == the sender's email address # you == the recipient's email address msg['Subject'] = 'The contents of is this...' msg['To'] = "TO toAddress@oststrom.com" msg['From'] = "FROM fromAddress@oststrom.com" msg['MyHEader'] = "hi :: hi" msg["m\r\nh"] = "yo" print(msg) print(repr(msg)) print("=========================") import email msg = email.message_from_string(str(msg)) msg.add_header("CUSTOM-HEADER: yo\r\n\nX-INJECTED: injected-header\r\naa","data") print(msg) print("-> FROM: %s" % msg.get("From")) print("-> TO: %s" % msg["To"]) print("-> MSG: " + repr(msg.get_payload())) ``` Output: `h: yo` and `X-INJECTED:` are injected ``` ⇒ python3 a.py Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: The contents of is this... To: TO toAddress@oststrom.com From: FROM fromAddress@oststrom.com MyHEader: hi :: hi m h: yo REAL_MSG_BODY_BEGIN ... REAL_MSG_BODY_END <email.mime.text.MIMEText object at 0x10076d850> ========================= Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: The contents of is this... To: TO toAddress@oststrom.com From: FROM fromAddress@oststrom.com MyHEader: hi :: hi CUSTOM-HEADER: yo X-INJECTED: injected-header aa: data m h: yo REAL_MSG_BODY_BEGIN ... REAL_MSG_BODY_END -> FROM: FROM fromAddress@oststrom.com -> TO: TO toAddress@oststrom.com -> MSG: 'm\r\nh: yo\n\nREAL_MSG_BODY_BEGIN\n...\nREAL_MSG_BODY_END' ``` ## Proposed Fix - reject `\n` in header keys - encode `\n` in header values to `\n\s+...` to signal a continuation of the header value. reject `\n+` ## Vendor Response Vendor response: ``` I discussed the vulnerability in private with one of the email module maintainers and he considers that it's not a vulnerability. Would you mind opening a public issue at https://bugs.python.org/ so the discussion can be recorded in public? Victor ``` ### Timeline ``` JUL/02/2020 - contact psrt; provided details, PoC, proposed patch AUG/20/2020 - vendor response: forwarded to maintainer of module SEP/15/2020 - vendor response: not a security issue ``` ## References * [1] https://www.python.org/ * [2] https://www.python.org/downloads/ |
|||
msg386589 - (view) | Author: Ross Rhodes (trrhodes) * | Date: 2021-02-07 13:23 | |
Hi Martin, Looking into this further, it appears we already catch CR-LF characters in header values, but your test case shows that we do not run the same checks on header names. I've opened a PR to rectify this - feel free to leave feedback. Ross |
|||
msg390867 - (view) | Author: Ross Rhodes (trrhodes) * | Date: 2021-04-12 16:50 | |
PR now “stale” since I have not received any feedback, yet. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:59:41 | admin | set | github: 87289 |
2021-04-12 16:50:21 | trrhodes | set | messages: + msg390867 |
2021-02-07 13:23:23 | trrhodes | set | messages: + msg386589 |
2021-02-07 13:19:33 | trrhodes | set | keywords:
+ patch nosy: + trrhodes pull_requests: + pull_request23267 stage: patch review |
2021-02-04 12:48:46 | martin.ortner | create |