| From fa88d415b500b5597c8eb99db3ff92c7d0c577f9 Mon Sep 17 00:00:00 2001 |
| From: Serhiy Storchaka <storchaka@gmail.com> |
| Date: Sat, 17 Aug 2024 16:30:52 +0300 |
| Subject: [PATCH] gh-123067: Fix quadratic complexity in parsing "-quoted |
| cookie values with backslashes (GH-123075) |
| |
| This fixes CVE-2024-7592. |
| (cherry picked from commit 44e458357fca05ca0ae2658d62c8c595b048b5ef) |
| |
| Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> |
| --- |
| Lib/http/cookies.py | 34 ++++------------- |
| Lib/test/test_http_cookies.py | 38 +++++++++++++++++++ |
| ...-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst | 1 + |
| 3 files changed, 47 insertions(+), 26 deletions(-) |
| create mode 100644 Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst |
| |
| diff --git a/Lib/http/cookies.py b/Lib/http/cookies.py |
| index 6694f5478bdadf..7ad62791a6ff52 100644 |
| --- a/Lib/http/cookies.py |
| +++ b/Lib/http/cookies.py |
| @@ -183,8 +183,13 @@ def _quote(str): |
| return '"' + str.translate(_Translator) + '"' |
| |
| |
| -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") |
| -_QuotePatt = re.compile(r"[\\].") |
| +_unquote_sub = re.compile(r'\\(?:([0-3][0-7][0-7])|(.))').sub |
| + |
| +def _unquote_replace(m): |
| + if m[1]: |
| + return chr(int(m[1], 8)) |
| + else: |
| + return m[2] |
| |
| def _unquote(str): |
| # If there aren't any doublequotes, |
| @@ -204,30 +209,7 @@ def _unquote(str): |
| # \012 --> \n |
| # \" --> " |
| # |
| - i = 0 |
| - n = len(str) |
| - res = [] |
| - while 0 <= i < n: |
| - o_match = _OctalPatt.search(str, i) |
| - q_match = _QuotePatt.search(str, i) |
| - if not o_match and not q_match: # Neither matched |
| - res.append(str[i:]) |
| - break |
| - # else: |
| - j = k = -1 |
| - if o_match: |
| - j = o_match.start(0) |
| - if q_match: |
| - k = q_match.start(0) |
| - if q_match and (not o_match or k < j): # QuotePatt matched |
| - res.append(str[i:k]) |
| - res.append(str[k+1]) |
| - i = k + 2 |
| - else: # OctalPatt matched |
| - res.append(str[i:j]) |
| - res.append(chr(int(str[j+1:j+4], 8))) |
| - i = j + 4 |
| - return _nulljoin(res) |
| + return _unquote_sub(_unquote_replace, str) |
| |
| # The _getdate() routine is used to set the expiration time in the cookie's HTTP |
| # header. By default, _getdate() returns the current time in the appropriate |
| diff --git a/Lib/test/test_http_cookies.py b/Lib/test/test_http_cookies.py |
| index 6072c7e15e92be..644e75cd5b742e 100644 |
| --- a/Lib/test/test_http_cookies.py |
| +++ b/Lib/test/test_http_cookies.py |
| @@ -5,6 +5,7 @@ |
| import unittest |
| from http import cookies |
| import pickle |
| +from test import support |
| |
| |
| class CookieTests(unittest.TestCase): |
| @@ -58,6 +59,43 @@ def test_basic(self): |
| for k, v in sorted(case['dict'].items()): |
| self.assertEqual(C[k].value, v) |
| |
| + def test_unquote(self): |
| + cases = [ |
| + (r'a="b=\""', 'b="'), |
| + (r'a="b=\\"', 'b=\\'), |
| + (r'a="b=\="', 'b=='), |
| + (r'a="b=\n"', 'b=n'), |
| + (r'a="b=\042"', 'b="'), |
| + (r'a="b=\134"', 'b=\\'), |
| + (r'a="b=\377"', 'b=\xff'), |
| + (r'a="b=\400"', 'b=400'), |
| + (r'a="b=\42"', 'b=42'), |
| + (r'a="b=\\042"', 'b=\\042'), |
| + (r'a="b=\\134"', 'b=\\134'), |
| + (r'a="b=\\\""', 'b=\\"'), |
| + (r'a="b=\\\042"', 'b=\\"'), |
| + (r'a="b=\134\""', 'b=\\"'), |
| + (r'a="b=\134\042"', 'b=\\"'), |
| + ] |
| + for encoded, decoded in cases: |
| + with self.subTest(encoded): |
| + C = cookies.SimpleCookie() |
| + C.load(encoded) |
| + self.assertEqual(C['a'].value, decoded) |
| + |
| + @support.requires_resource('cpu') |
| + def test_unquote_large(self): |
| + n = 10**6 |
| + for encoded in r'\\', r'\134': |
| + with self.subTest(encoded): |
| + data = 'a="b=' + encoded*n + ';"' |
| + C = cookies.SimpleCookie() |
| + C.load(data) |
| + value = C['a'].value |
| + self.assertEqual(value[:3], 'b=\\') |
| + self.assertEqual(value[-2:], '\\;') |
| + self.assertEqual(len(value), n + 3) |
| + |
| def test_load(self): |
| C = cookies.SimpleCookie() |
| C.load('Customer="WILE_E_COYOTE"; Version=1; Path=/acme') |
| diff --git a/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst b/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst |
| new file mode 100644 |
| index 00000000000000..6a234561fe31a3 |
| --- /dev/null |
| +++ b/Misc/NEWS.d/next/Library/2024-08-16-19-13-21.gh-issue-123067.Nx9O4R.rst |
| @@ -0,0 +1 @@ |
| +Fix quadratic complexity in parsing ``"``-quoted cookie values with backslashes by :mod:`http.cookies`. |