!130 fix CVE-2021-4189 CVE-2022-0391
From: @tong_1001 Reviewed-by: @hanxinke Signed-off-by: @hanxinke
This commit is contained in:
commit
6f1ffbd1fe
141
backport-CVE-2021-4189.patch
Normal file
141
backport-CVE-2021-4189.patch
Normal file
@ -0,0 +1,141 @@
|
||||
From 79373951b3eab585d42e0f0ab83718cbe1d0ee33 Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Tue, 16 Mar 2021 14:19:55 -0700
|
||||
Subject: [PATCH] [3.7] bpo-43285 Make ftplib not trust the PASV response.
|
||||
(GH-24838) (GH-24881) (GH-24883)
|
||||
|
||||
The IPv4 address value returned from the server in response to the PASV command
|
||||
should not be trusted. This prevents a malicious FTP server from using the
|
||||
response to probe IPv4 address and port combinations on the client network.
|
||||
|
||||
Instead of using the returned address, we use the IP address we're
|
||||
already connected to. This is the strategy other ftp clients adopted,
|
||||
and matches the only strategy available for the modern IPv6 EPSV command
|
||||
where the server response must return a port number and nothing else.
|
||||
|
||||
For the rare user who _wants_ this ugly behavior, set a `trust_server_pasv_ipv4_address`
|
||||
attribute on your `ftplib.FTP` instance to True..
|
||||
(cherry picked from commit 0ab152c6b5d95caa2dc1a30fa96e10258b5f188e)
|
||||
|
||||
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||
(cherry picked from commit 664d1d16274b47eea6ec92572e1ebf3939a6fa0c)
|
||||
---
|
||||
Doc/whatsnew/3.7.rst | 9 +++++++
|
||||
Lib/ftplib.py | 9 ++++++-
|
||||
Lib/test/test_ftplib.py | 27 ++++++++++++++++++-
|
||||
.../2021-03-13-03-48-14.bpo-43285.g-Hah3.rst | 8 ++++++
|
||||
4 files changed, 51 insertions(+), 2 deletions(-)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
|
||||
|
||||
diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst
|
||||
index 25e231dcc7dfa..85f924b2e41f3 100644
|
||||
--- a/Doc/whatsnew/3.7.rst
|
||||
+++ b/Doc/whatsnew/3.7.rst
|
||||
@@ -2585,3 +2585,12 @@ separator key, with ``&`` as the default. This change also affects
|
||||
functions internally. For more details, please see their respective
|
||||
documentation.
|
||||
(Contributed by Adam Goldschmidt, Senthil Kumaran and Ken Jin in :issue:`42967`.)
|
||||
+
|
||||
+Notable changes in Python 3.7.11
|
||||
+================================
|
||||
+
|
||||
+A security fix alters the :class:`ftplib.FTP` behavior to not trust the
|
||||
+IPv4 address sent from the remote server when setting up a passive data
|
||||
+channel. We reuse the ftp server IP address instead. For unusual code
|
||||
+requiring the old behavior, set a ``trust_server_pasv_ipv4_address``
|
||||
+attribute on your FTP instance to ``True``. (See :issue:`43285`)
|
||||
diff --git a/Lib/ftplib.py b/Lib/ftplib.py
|
||||
index 9611282ecacb2..83ed0aa9c140e 100644
|
||||
--- a/Lib/ftplib.py
|
||||
+++ b/Lib/ftplib.py
|
||||
@@ -104,6 +104,8 @@ class FTP:
|
||||
welcome = None
|
||||
passiveserver = 1
|
||||
encoding = "latin-1"
|
||||
+ # Disables https://bugs.python.org/issue43285 security if set to True.
|
||||
+ trust_server_pasv_ipv4_address = False
|
||||
|
||||
# Initialization method (called by class instantiation).
|
||||
# Initialize host to localhost, port to standard ftp port
|
||||
@@ -333,8 +335,13 @@ def makeport(self):
|
||||
return sock
|
||||
|
||||
def makepasv(self):
|
||||
+ """Internal: Does the PASV or EPSV handshake -> (address, port)"""
|
||||
if self.af == socket.AF_INET:
|
||||
- host, port = parse227(self.sendcmd('PASV'))
|
||||
+ untrusted_host, port = parse227(self.sendcmd('PASV'))
|
||||
+ if self.trust_server_pasv_ipv4_address:
|
||||
+ host = untrusted_host
|
||||
+ else:
|
||||
+ host = self.sock.getpeername()[0]
|
||||
else:
|
||||
host, port = parse229(self.sendcmd('EPSV'), self.sock.getpeername())
|
||||
return host, port
|
||||
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
|
||||
index da8ba32917be7..73a6250a5e4ae 100644
|
||||
--- a/Lib/test/test_ftplib.py
|
||||
+++ b/Lib/test/test_ftplib.py
|
||||
@@ -94,6 +94,10 @@ def __init__(self, conn):
|
||||
self.rest = None
|
||||
self.next_retr_data = RETR_DATA
|
||||
self.push('220 welcome')
|
||||
+ # We use this as the string IPv4 address to direct the client
|
||||
+ # to in response to a PASV command. To test security behavior.
|
||||
+ # https://bugs.python.org/issue43285/.
|
||||
+ self.fake_pasv_server_ip = '252.253.254.255'
|
||||
|
||||
def collect_incoming_data(self, data):
|
||||
self.in_buffer.append(data)
|
||||
@@ -136,7 +140,8 @@ def cmd_pasv(self, arg):
|
||||
sock.bind((self.socket.getsockname()[0], 0))
|
||||
sock.listen()
|
||||
sock.settimeout(TIMEOUT)
|
||||
- ip, port = sock.getsockname()[:2]
|
||||
+ port = sock.getsockname()[1]
|
||||
+ ip = self.fake_pasv_server_ip
|
||||
ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256
|
||||
self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2))
|
||||
conn, addr = sock.accept()
|
||||
@@ -698,6 +703,26 @@ def test_makepasv(self):
|
||||
# IPv4 is in use, just make sure send_epsv has not been used
|
||||
self.assertEqual(self.server.handler_instance.last_received_cmd, 'pasv')
|
||||
|
||||
+ def test_makepasv_issue43285_security_disabled(self):
|
||||
+ """Test the opt-in to the old vulnerable behavior."""
|
||||
+ self.client.trust_server_pasv_ipv4_address = True
|
||||
+ bad_host, port = self.client.makepasv()
|
||||
+ self.assertEqual(
|
||||
+ bad_host, self.server.handler_instance.fake_pasv_server_ip)
|
||||
+ # Opening and closing a connection keeps the dummy server happy
|
||||
+ # instead of timing out on accept.
|
||||
+ socket.create_connection((self.client.sock.getpeername()[0], port),
|
||||
+ timeout=TIMEOUT).close()
|
||||
+
|
||||
+ def test_makepasv_issue43285_security_enabled_default(self):
|
||||
+ self.assertFalse(self.client.trust_server_pasv_ipv4_address)
|
||||
+ trusted_host, port = self.client.makepasv()
|
||||
+ self.assertNotEqual(
|
||||
+ trusted_host, self.server.handler_instance.fake_pasv_server_ip)
|
||||
+ # Opening and closing a connection keeps the dummy server happy
|
||||
+ # instead of timing out on accept.
|
||||
+ socket.create_connection((trusted_host, port), timeout=TIMEOUT).close()
|
||||
+
|
||||
def test_with_statement(self):
|
||||
self.client.quit()
|
||||
|
||||
diff --git a/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst b/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
|
||||
new file mode 100644
|
||||
index 0000000000000..8312b7e885441
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Security/2021-03-13-03-48-14.bpo-43285.g-Hah3.rst
|
||||
@@ -0,0 +1,8 @@
|
||||
+:mod:`ftplib` no longer trusts the IP address value returned from the server
|
||||
+in response to the PASV command by default. This prevents a malicious FTP
|
||||
+server from using the response to probe IPv4 address and port combinations
|
||||
+on the client network.
|
||||
+
|
||||
+Code that requires the former vulnerable behavior may set a
|
||||
+``trust_server_pasv_ipv4_address`` attribute on their
|
||||
+:class:`ftplib.FTP` instances to ``True`` to re-enable it.
|
||||
170
backport-CVE-2022-0391.patch
Normal file
170
backport-CVE-2022-0391.patch
Normal file
@ -0,0 +1,170 @@
|
||||
From f4dac7ec55477a6c5d965e594e74bd6bda786903 Mon Sep 17 00:00:00 2001
|
||||
From: "Miss Islington (bot)"
|
||||
<31488909+miss-islington@users.noreply.github.com>
|
||||
Date: Thu, 6 May 2021 09:52:36 -0700
|
||||
Subject: [PATCH] [3.7] bpo-43882 - urllib.parse should sanitize urls
|
||||
containing ASCII newline and tabs. (GH-25923)
|
||||
|
||||
Co-authored-by: Gregory P. Smith <greg@krypto.org>
|
||||
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
||||
(cherry picked from commit 76cd81d60310d65d01f9d7b48a8985d8ab89c8b4)
|
||||
Co-authored-by: Senthil Kumaran <senthil@uthcode.com>
|
||||
(cherry picked from commit 515a7bc4e13645d0945b46a8e1d9102b918cd407)
|
||||
|
||||
Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
|
||||
---
|
||||
Doc/library/urllib.parse.rst | 13 +++++
|
||||
Lib/test/test_urlparse.py | 48 +++++++++++++++++++
|
||||
Lib/urllib/parse.py | 10 ++++
|
||||
.../2021-04-25-07-46-37.bpo-43882.Jpwx85.rst | 6 +++
|
||||
4 files changed, 77 insertions(+)
|
||||
create mode 100644 Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
|
||||
|
||||
diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst
|
||||
index e79faf035b06b..aea6505904be4 100644
|
||||
--- a/Doc/library/urllib.parse.rst
|
||||
+++ b/Doc/library/urllib.parse.rst
|
||||
@@ -311,6 +311,9 @@ or on combining URL components into a URL string.
|
||||
``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is
|
||||
decomposed before parsing, no error will be raised.
|
||||
|
||||
+ Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline
|
||||
+ ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL.
|
||||
+
|
||||
.. versionchanged:: 3.6
|
||||
Out-of-range port numbers now raise :exc:`ValueError`, instead of
|
||||
returning :const:`None`.
|
||||
@@ -319,6 +322,10 @@ or on combining URL components into a URL string.
|
||||
Characters that affect netloc parsing under NFKC normalization will
|
||||
now raise :exc:`ValueError`.
|
||||
|
||||
+ .. versionchanged:: 3.7.11
|
||||
+ ASCII newline and tab characters are stripped from the URL.
|
||||
+
|
||||
+.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser
|
||||
|
||||
.. function:: urlunsplit(parts)
|
||||
|
||||
@@ -660,6 +667,10 @@ task isn't already covered by the URL parsing functions above.
|
||||
|
||||
.. seealso::
|
||||
|
||||
+ `WHATWG`_ - URL Living standard
|
||||
+ Working Group for the URL Standard that defines URLs, domains, IP addresses, the
|
||||
+ application/x-www-form-urlencoded format, and their API.
|
||||
+
|
||||
:rfc:`3986` - Uniform Resource Identifiers
|
||||
This is the current standard (STD66). Any changes to urllib.parse module
|
||||
should conform to this. Certain deviations could be observed, which are
|
||||
@@ -683,3 +694,5 @@ task isn't already covered by the URL parsing functions above.
|
||||
|
||||
:rfc:`1738` - Uniform Resource Locators (URL)
|
||||
This specifies the formal syntax and semantics of absolute URLs.
|
||||
+
|
||||
+.. _WHATWG: https://url.spec.whatwg.org/
|
||||
diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py
|
||||
index e3088b2f39bd7..3509278a01694 100644
|
||||
--- a/Lib/test/test_urlparse.py
|
||||
+++ b/Lib/test/test_urlparse.py
|
||||
@@ -612,6 +612,54 @@ def test_urlsplit_attributes(self):
|
||||
with self.assertRaisesRegex(ValueError, "out of range"):
|
||||
p.port
|
||||
|
||||
+ def test_urlsplit_remove_unsafe_bytes(self):
|
||||
+ # Remove ASCII tabs and newlines from input, for http common case scenario.
|
||||
+ url = "h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urllib.parse.urlsplit(url)
|
||||
+ self.assertEqual(p.scheme, "http")
|
||||
+ self.assertEqual(p.netloc, "www.python.org")
|
||||
+ self.assertEqual(p.path, "/javascript:alert('msg')/")
|
||||
+ self.assertEqual(p.query, "query=something")
|
||||
+ self.assertEqual(p.fragment, "fragment")
|
||||
+ self.assertEqual(p.username, None)
|
||||
+ self.assertEqual(p.password, None)
|
||||
+ self.assertEqual(p.hostname, "www.python.org")
|
||||
+ self.assertEqual(p.port, None)
|
||||
+ self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Remove ASCII tabs and newlines from input as bytes, for http common case scenario.
|
||||
+ url = b"h\nttp://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urllib.parse.urlsplit(url)
|
||||
+ self.assertEqual(p.scheme, b"http")
|
||||
+ self.assertEqual(p.netloc, b"www.python.org")
|
||||
+ self.assertEqual(p.path, b"/javascript:alert('msg')/")
|
||||
+ self.assertEqual(p.query, b"query=something")
|
||||
+ self.assertEqual(p.fragment, b"fragment")
|
||||
+ self.assertEqual(p.username, None)
|
||||
+ self.assertEqual(p.password, None)
|
||||
+ self.assertEqual(p.hostname, b"www.python.org")
|
||||
+ self.assertEqual(p.port, None)
|
||||
+ self.assertEqual(p.geturl(), b"http://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # any scheme
|
||||
+ url = "x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urllib.parse.urlsplit(url)
|
||||
+ self.assertEqual(p.geturl(), "x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Remove ASCII tabs and newlines from input as bytes, any scheme.
|
||||
+ url = b"x-new-scheme\t://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ p = urllib.parse.urlsplit(url)
|
||||
+ self.assertEqual(p.geturl(), b"x-new-scheme://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
+ # Unsafe bytes is not returned from urlparse cache.
|
||||
+ # scheme is stored after parsing, sending an scheme with unsafe bytes *will not* return an unsafe scheme
|
||||
+ url = "https://www.python\n.org\t/java\nscript:\talert('msg\r\n')/?query\n=\tsomething#frag\nment"
|
||||
+ scheme = "htt\nps"
|
||||
+ for _ in range(2):
|
||||
+ p = urllib.parse.urlsplit(url, scheme=scheme)
|
||||
+ self.assertEqual(p.scheme, "https")
|
||||
+ self.assertEqual(p.geturl(), "https://www.python.org/javascript:alert('msg')/?query=something#fragment")
|
||||
+
|
||||
def test_attributes_bad_port(self):
|
||||
"""Check handling of invalid ports."""
|
||||
for bytes in (False, True):
|
||||
diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py
|
||||
index e67d69db3614b..4f21ce784eee9 100644
|
||||
--- a/Lib/urllib/parse.py
|
||||
+++ b/Lib/urllib/parse.py
|
||||
@@ -76,6 +76,9 @@
|
||||
'0123456789'
|
||||
'+-.')
|
||||
|
||||
+# Unsafe bytes to be removed per WHATWG spec
|
||||
+_UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n']
|
||||
+
|
||||
# XXX: Consider replacing with functools.lru_cache
|
||||
MAX_CACHE_SIZE = 20
|
||||
_parse_cache = {}
|
||||
@@ -409,6 +412,11 @@ def _checknetloc(netloc):
|
||||
raise ValueError("netloc '" + netloc + "' contains invalid " +
|
||||
"characters under NFKC normalization")
|
||||
|
||||
+def _remove_unsafe_bytes_from_url(url):
|
||||
+ for b in _UNSAFE_URL_BYTES_TO_REMOVE:
|
||||
+ url = url.replace(b, "")
|
||||
+ return url
|
||||
+
|
||||
def urlsplit(url, scheme='', allow_fragments=True):
|
||||
"""Parse a URL into 5 components:
|
||||
<scheme>://<netloc>/<path>?<query>#<fragment>
|
||||
@@ -416,6 +424,8 @@ def urlsplit(url, scheme='', allow_fragments=True):
|
||||
Note that we don't break the components up in smaller bits
|
||||
(e.g. netloc is a single string) and we don't expand % escapes."""
|
||||
url, scheme, _coerce_result = _coerce_args(url, scheme)
|
||||
+ url = _remove_unsafe_bytes_from_url(url)
|
||||
+ scheme = _remove_unsafe_bytes_from_url(scheme)
|
||||
allow_fragments = bool(allow_fragments)
|
||||
key = url, scheme, allow_fragments, type(url), type(scheme)
|
||||
cached = _parse_cache.get(key, None)
|
||||
diff --git a/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst b/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
|
||||
new file mode 100644
|
||||
index 0000000000000..a326d079dff4a
|
||||
--- /dev/null
|
||||
+++ b/Misc/NEWS.d/next/Security/2021-04-25-07-46-37.bpo-43882.Jpwx85.rst
|
||||
@@ -0,0 +1,6 @@
|
||||
+The presence of newline or tab characters in parts of a URL could allow
|
||||
+some forms of attacks.
|
||||
+
|
||||
+Following the controlling specification for URLs defined by WHATWG
|
||||
+:func:`urllib.parse` now removes ASCII newlines and tabs from URLs,
|
||||
+preventing such attacks.
|
||||
13
python3.spec
13
python3.spec
@ -3,7 +3,7 @@ Summary: Interpreter of the Python3 programming language
|
||||
URL: https://www.python.org/
|
||||
|
||||
Version: 3.7.9
|
||||
Release: 19
|
||||
Release: 20
|
||||
License: Python
|
||||
|
||||
%global branchversion 3.7
|
||||
@ -152,6 +152,8 @@ Patch6042: backport-36356-Fix-memory-leak-in-_asynciomodule.c-GH-165.patch
|
||||
Patch6043: backport-CVE-2021-3733.patch
|
||||
Patch6044: backport-CVE-2021-3737.patch
|
||||
Patch6045: backport-bpo-44022-Improve-the-regression-test.patch
|
||||
Patch6046: backport-CVE-2021-4189.patch
|
||||
Patch6047: backport-CVE-2022-0391.patch
|
||||
|
||||
patch9000: Don-t-override-PYTHONPATH-which-is-already-set.patch
|
||||
patch9001: add-the-sm3-method-for-obtaining-the-salt-value.patch
|
||||
@ -292,6 +294,9 @@ rm Lib/ensurepip/_bundled/*.whl
|
||||
%patch6043 -p1
|
||||
%patch6044 -p1
|
||||
%patch6045 -p1
|
||||
%patch6046 -p1
|
||||
%patch6047 -p1
|
||||
|
||||
%patch9000 -p1
|
||||
%patch9001 -p1
|
||||
|
||||
@ -895,6 +900,12 @@ export BEP_GTDLIST="$BEP_GTDLIST_TMP"
|
||||
%{_mandir}/*/*
|
||||
|
||||
%changelog
|
||||
* Thu Feb 17 2022 shixuantong <shixuantong@h-partners.com> - 3.7.9-20
|
||||
- Type:CVE
|
||||
- CVE:CVE-2021-4189 CVE-2022-0391
|
||||
- SUG:NA
|
||||
- DESC:fix CVE-2021-4189 CVE-2022-0391
|
||||
|
||||
* Thu Feb 10 2022 shixuantong <shixuantong@h-partners.com> - 3.7.9-19
|
||||
- Type:bugfix
|
||||
- CVE:NA
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user