summaryrefslogtreecommitdiffstats
path: root/recipes-devtools/python/python/CVE-2018-1060-CVE-2018-1061-2.7-bpo-32981-Fix-catastrophic-backtracking-vulns-GH.patch
blob: 6239503eebe38a4719c7248a1bf9e248c1051e47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
From fbfdc20005366facc079675ee7e217a0993ef2f9 Mon Sep 17 00:00:00 2001
From: Andreas Wellving <andreas.wellving@enea.com>
Date: Mon, 22 Oct 2018 13:44:16 +0200
Subject: [PATCH] [2.7] bpo-32981: Fix catastrophic backtracking vulns
 (GH-5955)

* Prevent low-grade poplib REDOS (CVE-2018-1060)

The regex to test a mail server's timestamp is susceptible to
catastrophic backtracking on long evil responses from the server.

Happily, the maximum length of malicious inputs is 2K thanks
to a limit introduced in the fix for CVE-2013-1752.

A 2KB evil response from the mail server would result in small slowdowns
(milliseconds vs. microseconds) accumulated over many apop calls.
This is a potential DOS vector via accumulated slowdowns.

Replace it with a similar non-vulnerable regex.

The new regex is RFC compliant.
The old regex was non-compliant in edge cases.

* Prevent difflib REDOS (CVE-2018-1061)

The default regex for IS_LINE_JUNK is susceptible to
catastrophic backtracking.
This is a potential DOS vector.

Replace it with an equivalent non-vulnerable regex.

Also introduce unit and REDOS tests for difflib.

CVE: CVE-2018-1060
CVE: CVE-2018-1061
Upstream-Status: Backport [https://github.com/python/cpython/commit/937ac1fe069a4dc8471dff205f553d82e724015b]

Co-authored-by: Tim Peters <tim.peters@gmail.com>
Co-authored-by: Christian Heimes <christian@python.org>.
(cherry picked from commit 0e6c8ee2358a2e23117501826c008842acb835ac)
Signed-off-by: Andreas Wellving <andreas.wellving@enea.com>
---
 Lib/difflib.py                                     |  2 +-
 Lib/poplib.py                                      |  2 +-
 Lib/test/test_difflib.py                           | 22 +++++++++++++++++++++-
 Lib/test/test_poplib.py                            | 10 ++++++++++
 Misc/ACKS                                          |  1 +
 .../2018-03-02-10-24-52.bpo-32981.O_qDyj.rst       |  4 ++++
 6 files changed, 38 insertions(+), 3 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Security/2018-03-02-10-24-52.bpo-32981.O_qDyj.rst

diff --git a/Lib/difflib.py b/Lib/difflib.py
index 1c6fbdb..788a92d 100644
--- a/Lib/difflib.py
+++ b/Lib/difflib.py
@@ -1103,7 +1103,7 @@ class Differ:
 
 import re
 
-def IS_LINE_JUNK(line, pat=re.compile(r"\s*#?\s*$").match):
+def IS_LINE_JUNK(line, pat=re.compile(r"\s*(?:#\s*)?$").match):
     r"""
     Return 1 for ignorable line: iff `line` is blank or contains a single '#'.
 
diff --git a/Lib/poplib.py b/Lib/poplib.py
index b91e5f7..a238510 100644
--- a/Lib/poplib.py
+++ b/Lib/poplib.py
@@ -274,7 +274,7 @@ class POP3:
         return self._shortcmd('RPOP %s' % user)
 
 
-    timestamp = re.compile(r'\+OK.*(<[^>]+>)')
+    timestamp = re.compile(br'\+OK.[^<]*(<.*>)')
 
     def apop(self, user, secret):
         """Authorisation
diff --git a/Lib/test/test_difflib.py b/Lib/test/test_difflib.py
index 35f2c36..d8277b7 100644
--- a/Lib/test/test_difflib.py
+++ b/Lib/test/test_difflib.py
@@ -269,13 +269,33 @@ class TestOutputFormat(unittest.TestCase):
         self.assertEqual(fmt(3,6), '4,6')
         self.assertEqual(fmt(0,0), '0')
 
+class TestJunkAPIs(unittest.TestCase):
+    def test_is_line_junk_true(self):
+        for line in ['#', '  ', ' #', '# ', ' # ', '']:
+            self.assertTrue(difflib.IS_LINE_JUNK(line), repr(line))
+
+    def test_is_line_junk_false(self):
+        for line in ['##', ' ##', '## ', 'abc ', 'abc #', 'Mr. Moose is up!']:
+            self.assertFalse(difflib.IS_LINE_JUNK(line), repr(line))
+
+    def test_is_line_junk_REDOS(self):
+        evil_input = ('\t' * 1000000) + '##'
+        self.assertFalse(difflib.IS_LINE_JUNK(evil_input))
+
+    def test_is_character_junk_true(self):
+        for char in [' ', '\t']:
+            self.assertTrue(difflib.IS_CHARACTER_JUNK(char), repr(char))
+
+    def test_is_character_junk_false(self):
+        for char in ['a', '#', '\n', '\f', '\r', '\v']:
+            self.assertFalse(difflib.IS_CHARACTER_JUNK(char), repr(char))
 
 def test_main():
     difflib.HtmlDiff._default_prefix = 0
     Doctests = doctest.DocTestSuite(difflib)
     run_unittest(
         TestWithAscii, TestAutojunk, TestSFpatches, TestSFbugs,
-        TestOutputFormat, Doctests)
+        TestOutputFormat, TestJunkAPIs)
 
 if __name__ == '__main__':
     test_main()
diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py
index 23d6887..d214375 100644
--- a/Lib/test/test_poplib.py
+++ b/Lib/test/test_poplib.py
@@ -211,6 +211,16 @@ class TestPOP3Class(TestCase):
     def test_rpop(self):
         self.assertOK(self.client.rpop('foo'))
 
+    def test_apop_REDOS(self):
+        # Replace welcome with very long evil welcome.
+        # NB The upper bound on welcome length is currently 2048.
+        # At this length, evil input makes each apop call take
+        # on the order of milliseconds instead of microseconds.
+        evil_welcome = b'+OK' + (b'<' * 1000000)
+        with test_support.swap_attr(self.client, 'welcome', evil_welcome):
+            # The evil welcome is invalid, so apop should throw.
+            self.assertRaises(poplib.error_proto, self.client.apop, 'a', 'kb')
+
     def test_top(self):
         expected =  ('+OK 116 bytes',
                      ['From: postmaster@python.org', 'Content-Type: text/plain',
diff --git a/Misc/ACKS b/Misc/ACKS
index 9cbc230..952d6dd 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -314,6 +314,7 @@ Kushal Das
 Jonathan Dasteel
 Pierre-Yves David
 A. Jesse Jiryu Davis
+Jamie (James C.) Davis
 Merlijn van Deen
 John DeGood
 Ned Deily
diff --git a/Misc/NEWS.d/next/Security/2018-03-02-10-24-52.bpo-32981.O_qDyj.rst b/Misc/NEWS.d/next/Security/2018-03-02-10-24-52.bpo-32981.O_qDyj.rst
new file mode 100644
index 0000000..9ebabb4
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2018-03-02-10-24-52.bpo-32981.O_qDyj.rst
@@ -0,0 +1,4 @@
+Regexes in difflib and poplib were vulnerable to catastrophic backtracking.
+These regexes formed potential DOS vectors (REDOS). They have been
+refactored. This resolves CVE-2018-1060 and CVE-2018-1061.
+Patch by Jamie Davis.