-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The AbstractBasicAuthHandler class of the urllib.request module uses an inefficient regular expression which can be exploited by an attacker to cause a denial of service. Fix the regex to prevent the catastrophic backtracking. Vulnerability reported by Ben Caller and Matt Schwager. AbstractBasicAuthHandler of urllib.request now parses all WWW-Authenticate HTTP headers and accepts multiple challenges per header: use the realm of the first Basic challenge. Co-Authored-By: Serhiy Storchaka <storchaka@gmail.com>
- Loading branch information
1 parent
d57cf55
commit 0b297d4
Showing
4 changed files
with
115 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -937,8 +937,15 @@ class AbstractBasicAuthHandler: | |
|
||
# allow for double- and single-quoted realm values | ||
# (single quotes are a violation of the RFC, but appear in the wild) | ||
rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' | ||
'realm=(["\']?)([^"\']*)\\2', re.I) | ||
rx = re.compile('(?:^|,)' # start of the string or ',' | ||
'[ \t]*' # optional whitespaces | ||
'([^ \t]+)' # scheme like "Basic" | ||
'[ \t]+' # mandatory whitespaces | ||
# realm=xxx | ||
# realm='xxx' | ||
# realm="xxx" | ||
'realm=(["\']?)([^"\']*)\\2', | ||
re.I) | ||
|
||
# XXX could pre-emptively send auth info already accepted (RFC 2617, | ||
# end of section 2, and section 1.2 immediately after "credentials" | ||
|
@@ -950,27 +957,51 @@ def __init__(self, password_mgr=None): | |
self.passwd = password_mgr | ||
self.add_password = self.passwd.add_password | ||
|
||
def _parse_realm(self, header): | ||
# parse WWW-Authenticate header: accept multiple challenges per header | ||
found_challenge = False | ||
for mo in AbstractBasicAuthHandler.rx.finditer(header): | ||
scheme, quote, realm = mo.groups() | ||
if quote not in ['"', "'"]: | ||
warnings.warn("Basic Auth Realm was unquoted", | ||
UserWarning, 3) | ||
|
||
yield (scheme, realm) | ||
|
||
found_challenge = True | ||
|
||
if not found_challenge: | ||
if header: | ||
scheme = header.split()[0] | ||
else: | ||
scheme = '' | ||
yield (scheme, None) | ||
|
||
def http_error_auth_reqed(self, authreq, host, req, headers): | ||
# host may be an authority (without userinfo) or a URL with an | ||
# authority | ||
# XXX could be multiple headers | ||
authreq = headers.get(authreq, None) | ||
headers = headers.get_all(authreq) | ||
if not headers: | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
vstinner
Author
Member
|
||
# no header found | ||
return | ||
|
||
if authreq: | ||
scheme = authreq.split()[0] | ||
if scheme.lower() != 'basic': | ||
raise ValueError("AbstractBasicAuthHandler does not" | ||
" support the following scheme: '%s'" % | ||
scheme) | ||
else: | ||
mo = AbstractBasicAuthHandler.rx.search(authreq) | ||
if mo: | ||
scheme, quote, realm = mo.groups() | ||
if quote not in ['"',"'"]: | ||
warnings.warn("Basic Auth Realm was unquoted", | ||
UserWarning, 2) | ||
if scheme.lower() == 'basic': | ||
return self.retry_http_basic_auth(host, req, realm) | ||
unsupported = None | ||
for header in headers: | ||
for scheme, realm in self._parse_realm(header): | ||
if scheme.lower() != 'basic': | ||
unsupported = scheme | ||
continue | ||
|
||
if realm is not None: | ||
# Use the first matching Basic challenge. | ||
# Ignore following challenges even if they use the Basic | ||
# scheme. | ||
return self.retry_http_basic_auth(host, req, realm) | ||
|
||
if unsupported is not None: | ||
raise ValueError("AbstractBasicAuthHandler does not " | ||
"support the following scheme: %r" | ||
% (scheme,)) | ||
|
||
def retry_http_basic_auth(self, host, req, realm): | ||
user, pw = self.passwd.find_user_password(realm, host) | ||
|
3 changes: 3 additions & 0 deletions
3
Misc/NEWS.d/next/Library/2020-03-25-16-02-16.bpo-39503.YmMbYn.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
:class:`~urllib.request.AbstractBasicAuthHandler` of :mod:`urllib.request` | ||
now parses all WWW-Authenticate HTTP headers and accepts multiple challenges | ||
per header: use the realm of the first Basic challenge. |
5 changes: 5 additions & 0 deletions
5
Misc/NEWS.d/next/Security/2020-01-30-16-15-29.bpo-39503.B299Yq.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
CVE-2020-8492: The :class:`~urllib.request.AbstractBasicAuthHandler` class of the | ||
:mod:`urllib.request` module uses an inefficient regular expression which can | ||
be exploited by an attacker to cause a denial of service. Fix the regex to | ||
prevent the catastrophic backtracking. Vulnerability reported by Ben Caller | ||
and Matt Schwager. |
"headers" is a dict object? If so, the dict object does not seem to have no attribute "get_all" . @vstinner @web-flow @phsilva @aivarannamaa