From: Jamie Davis Date: Tue, 6 Mar 2018 05:59:02 +0000 (-0500) Subject: [2.7] closes bpo-32997: Fix REDOS in fpformat (GH-5984) X-Git-Tag: v2.7.15rc1~25 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=55d5bfba9482d39080f7b9ec3e6257ecd23f264f;p=python [2.7] closes bpo-32997: Fix REDOS in fpformat (GH-5984) The regex to decode a number in fpformat is susceptible to catastrophic backtracking. This is a potential DOS vector if a server is using fpformat on untrusted number strings. Replace it with an equivalent non-vulnerable regex. The match behavior of the new regex is slightly different. It captures the whole integer part of the number in one group, Leading zeros are stripped off later. --- diff --git a/Lib/fpformat.py b/Lib/fpformat.py index 71cbb25f3c..0537a27b88 100644 --- a/Lib/fpformat.py +++ b/Lib/fpformat.py @@ -19,7 +19,7 @@ import re __all__ = ["fix","sci","NotANumber"] # Compiled regular expression to "decode" a number -decoder = re.compile(r'^([-+]?)0*(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') +decoder = re.compile(r'^([-+]?)(\d*)((?:\.\d*)?)(([eE][-+]?\d+)?)$') # \0 the whole thing # \1 leading sign or empty # \2 digits left of decimal point @@ -41,6 +41,7 @@ def extract(s): res = decoder.match(s) if res is None: raise NotANumber, s sign, intpart, fraction, exppart = res.group(1,2,3,4) + intpart = intpart.lstrip('0'); if sign == '+': sign = '' if fraction: fraction = fraction[1:] if exppart: expo = int(exppart[1:]) diff --git a/Lib/test/test_fpformat.py b/Lib/test/test_fpformat.py index e6de3b0c11..428623ebb3 100644 --- a/Lib/test/test_fpformat.py +++ b/Lib/test/test_fpformat.py @@ -67,6 +67,16 @@ class FpformatTest(unittest.TestCase): else: self.fail("No exception on non-numeric sci") + def test_REDOS(self): + # This attack string will hang on the old decoder pattern. + attack = '+0' + ('0' * 1000000) + '++' + digs = 5 # irrelevant + + # fix returns input if it does not decode + self.assertEqual(fpformat.fix(attack, digs), attack) + # sci raises NotANumber + with self.assertRaises(NotANumber): + fpformat.sci(attack, digs) def test_main(): run_unittest(FpformatTest) diff --git a/Misc/NEWS.d/next/Security/2018-03-05-10-14-42.bpo-32997.hp2s8n.rst b/Misc/NEWS.d/next/Security/2018-03-05-10-14-42.bpo-32997.hp2s8n.rst new file mode 100644 index 0000000000..3c78ba61ae --- /dev/null +++ b/Misc/NEWS.d/next/Security/2018-03-05-10-14-42.bpo-32997.hp2s8n.rst @@ -0,0 +1,4 @@ +A regex in fpformat was vulnerable to catastrophic backtracking. This regex +was a potential DOS vector (REDOS). Based on typical uses of fpformat the +risk seems low. The regex has been refactored and is now safe. Patch by +Jamie Davis.