-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[security] Infinite loop on folding email (_fold_as_ew()) if an header has no spaces #77710
Comments
I just reported a bug about email folding at bpo-33524, but this issue is more fatal in some languages like Chinese or Japanese, which does not insert spaces between each words. Create an email with longer header than max_line_length set by its policy. And the header contains non-ascii characters but no white spaces. ^CTraceback (most recent call last):
File "emailtest.py", line 7, in <module>
policy.fold("Subject", msg["Subject"])
File "/usr/lib/python3.6/email/policy.py", line 183, in fold
return self._fold(name, value, refold_binary=True)
File "/usr/lib/python3.6/email/policy.py", line 205, in _fold
return value.fold(policy=self)
File "/usr/lib/python3.6/email/headerregistry.py", line 258, in fold
return header.fold(policy=policy)
File "/usr/lib/python3.6/email/_header_value_parser.py", line 144, in fold
return _refold_parse_tree(self, policy=policy)
File "/usr/lib/python3.6/email/_header_value_parser.py", line 2651, in _refold_parse_tree
part.ew_combine_allowed, charset)
File "/usr/lib/python3.6/email/_header_value_parser.py", line 2735, in _fold_as_ew
ew = _ew.encode(first_part)
File "/usr/lib/python3.6/email/_encoded_words.py", line 215, in encode
blen = _cte_encode_length['b'](bstring)
File "/usr/lib/python3.6/email/_encoded_words.py", line 130, in len_b
groups_of_3, leftover = divmod(len(bstring), 3)
KeyboardInterrupt Code to reproduce: from email.message import EmailMessage
from email.policy import default
policy = default # max_line_length = 78
msg = EmailMessage()
msg["Subject"] = "á"*100
policy.fold("Subject", msg["Subject"]) No problems in following cases:
|
I tried the test case on master branch. I ran the test case on 1GB RAM Linux based digitalocean droplet to have the script killed. Please find the results as below : # Python build ➜ cpython git:(master) ✗ ./python
# Test case ➜ cpython git:(master) ✗ cat foo.py from email.message import EmailMessage
from email.policy import default
policy = default # max_line_length = 78
msg = EmailMessage()
msg["Subject"] = "á"*100
policy.fold("Subject", msg["Subject"]) # Test case execution ➜ cpython git:(master) ✗ time ./python foo.py # I tried to do Ctrl + C after 2 minutes to stop and the stack trace is as below : ➜ cpython git:(master) ✗ time ./python foo.py
^CTraceback (most recent call last):
File "foo.py", line 7, in <module>
policy.fold("Subject", msg["Subject"])
File "/root/cpython/Lib/email/policy.py", line 183, in fold
return self._fold(name, value, refold_binary=True)
File "/root/cpython/Lib/email/policy.py", line 205, in _fold
return value.fold(policy=self)
File "/root/cpython/Lib/email/headerregistry.py", line 258, in fold
return header.fold(policy=policy)
File "/root/cpython/Lib/email/_header_value_parser.py", line 144, in fold
return _refold_parse_tree(self, policy=policy)
File "/root/cpython/Lib/email/_header_value_parser.py", line 2650, in _refold_parse_tree
part.ew_combine_allowed, charset)
File "/root/cpython/Lib/email/_header_value_parser.py", line 2728, in _fold_as_ew
ew = _ew.encode(first_part, charset=encode_as)
File "/root/cpython/Lib/email/_encoded_words.py", line 226, in encode
qlen = _cte_encode_length['q'](bstring)
File "/root/cpython/Lib/email/_encoded_words.py", line 93, in len_q
return sum(len(_q_byte_map[x]) for x in bstring)
File "/root/cpython/Lib/email/_encoded_words.py", line 93, in <genexpr>
return sum(len(_q_byte_map[x]) for x in bstring)
KeyboardInterrupt
./python foo.py 131.41s user 0.43s system 98% cpu 2:13.89 total Thanks |
Since it's a denial of service which can be triggered by an user, I mark this issue as a security issue. I can be wrong, but it seems like Python 2.7 isn't affected: Lib/email/_header_value_parser.py was added by bpo-12586 (commit 0b6f6c8). Python 2.7 doesn't have this file nor policies. |
Python 3.6, 3.5 and 2.7 are still vulnerable. Is there someone interested to backport the fix? |
It's unclear to me if Python 3.5 is affected or not. The fix changes the function _fold_as_ew(), Python 3.5 doesn't have this function *but* there is a call a _fold_as_ew() method!? Lib/email/_header_value_parser.py:427: in _fold() method
If I backport the 2 tests, they fail *but* they don't hang forever (they complete in less than 1 second). ====================================================================== Traceback (most recent call last):
File "/home/vstinner/prog/python/3.5/Lib/test/test_email/test_headerregistry.py", line 1601, in test_fold_overlong_words_using_RFC2047
'X-Report-Abuse: =?utf-8?q?=3Chttps=3A//www=2Emailitapp=2E'
AssertionError: 'X-Report-Abuse: <https://www.mailitapp.com/report_abuse.p[50 chars]x>\n' != 'X-Report-Abuse: =?utf-8?q?=3Chttps=3A//www=2Emailitapp=2E[114 chars]?=\n'
- X-Report-Abuse: <https://www.mailitapp.com/report_abuse.php?mid=xxx-xxx-xxxxxxxxxxxxxxxxxxxxxxxx==-xxx-xx-xx>
+ X-Report-Abuse: =?utf-8?q?=3Chttps=3A//www=2Emailitapp=2Ecom/report=5Fabuse?=
+ =?utf-8?q?=2Ephp=3Fmid=3Dxxx-xxx-xxxxxxxxxxxxxxxxxxxxxxxx=3D=3D-xxx-xx-xx?=
+ =?utf-8?q?=3E?= ====================================================================== Traceback (most recent call last):
File "/home/vstinner/prog/python/3.5/Lib/test/test_email/test_policy.py", line 241, in test_non_ascii_chars_do_not_cause_inf_loop
12 * ' =?utf-8?q?=C4=85?=\n')
AssertionError: 'Subject: =?utf-8?b?xIXEhcSFxIXEhcSFxIXEhcSFxIXEhcSF?=\n' != 'Subject: \n =?utf-8?q?=C4=85?=\n =?utf-8?q?=C4=85?[209 chars]?=\n'
- Subject: =?utf-8?b?xIXEhcSFxIXEhcSFxIXEhcSFxIXEhcSF?=
+ Subject:
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?=
+ =?utf-8?q?=C4=85?= |
Python 3.5 is not vulnerable, it doesn't hang on the following code: import email.policy
policy = email.policy.default.clone(max_line_length=20)
actual = policy.fold('Subject', '\u0105' * 12) |
Python 2.7 doesn't have email.policy module. For Python 2.7, I wrote this code: import email.header
import email.message
msg = email.message.Message()
msg.set_charset("UTF-8")
msg['Subject'] = email.header.Header(u'\u0105' * 12, maxlinelen=20, charset="UTF-8")
print(msg.as_string()) I get this output: --- I have no idea if this example says that Python 2.7 is vulnerable or not. I get a different output on the master branch: --- But I don't know if I use the email API properly. "Subject: =?utf-8?b?xIXEhcSFxIXEhcSFxIXEhcSFxIXEhcSF?=" is longer than 20 characters. |
Using git bisect, I found which commit introduced the regression, bpo-27240: commit a87ba60
The first vulnerable release is Python 3.6.4: Python 3.6.3 and older are not affected by this vulnerability. So yes, I confirm that Python 2.7 and 3.5 are not vulnerable. By the way, a backport to 3.5 was requested but rejected :-) I close the issue. Thanks Rad164 for the report and thanks Krzysztof Wojcik fo the fix! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: