Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[security][CVE-2020-8492] Denial of service in urllib.request.AbstractBasicAuthHandler #83684

Closed
vstinner opened this issue Jan 30, 2020 · 17 comments
Labels
3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes stdlib Python modules in the Lib dir type-security A security issue

Comments

@vstinner
Copy link
Member

BPO 39503
Nosy @vstinner, @larryhastings, @ned-deily, @mgorny, @koobs, @miss-islington, @ware, @tapakund, @bcaller
PRs
  • bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler #18284
  • [3.8] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19291
  • [3.7] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19292
  • [3.8] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19296
  • [3.7] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19297
  • [3.6] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler #19299
  • [3.5] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler #19301
  • [2.7] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler #19302
  • [3.6] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19304
  • [3.5] bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) #19305
  • Files
  • bench_parser.py
  • bench_parser2.py: Benchmark
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2020-06-20.08:33:12.125>
    created_at = <Date 2020-01-30.15:11:29.386>
    labels = ['type-security', '3.8', '3.9', '3.10', '3.11', '3.7', 'library']
    title = '[security][CVE-2020-8492] Denial of service in urllib.request.AbstractBasicAuthHandler'
    updated_at = <Date 2021-09-14.10:39:30.313>
    user = 'https://github.com/vstinner'

    bugs.python.org fields:

    activity = <Date 2021-09-14.10:39:30.313>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2020-06-20.08:33:12.125>
    closer = 'larry'
    components = ['Library (Lib)']
    creation = <Date 2020-01-30.15:11:29.386>
    creator = 'vstinner'
    dependencies = []
    files = ['49016', '49023']
    hgrepos = []
    issue_num = 39503
    keywords = ['patch']
    message_count = 17.0
    messages = ['361072', '361073', '361081', '361343', '364996', '365335', '365538', '365541', '365542', '365578', '365579', '365663', '371921', '401762', '401766', '401768', '401770']
    nosy_count = 11.0
    nosy_names = ['vstinner', 'larry', 'ned.deily', 'mgorny', 'koobs', 'miss-islington', 'ware', 'tapakund', 'Anselmo Melo', 'bc', 'sxt1001']
    pr_nums = ['18284', '19291', '19292', '19296', '19297', '19299', '19301', '19302', '19304', '19305']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'security'
    url = 'https://bugs.python.org/issue39503'
    versions = ['Python 3.7', 'Python 3.8', 'Python 3.9', 'Python 3.10', 'Python 3.11']

    @vstinner
    Copy link
    Member Author

    Copy of an email received on the Python Security Response team, 9 days ago. I consider that it's not worth it to have an embargo on this vulnerability, so I make it public.

    Hi there,

    I believe I've found a denial-of-service (DoS) bug in
    urllib.request.AbstractBasicAuthHandler. To start, I'm operating on some
    background information from this document: HTTP authentication
    <https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication\>. The bug
    itself is a ReDoS <https://www.regular-expressions.info/redos.html\> bug
    causing catastrophic backtracking. To reproduce the issue we can use the
    following code:

    from urllib.request import AbstractBasicAuthHandler
    auth_handler = AbstractBasicAuthHandler()
    auth_handler.http_error_auth_reqed(
        'www-authenticate',
        'unused',
        'unused',
        {
            'www-authenticate': 'Basic ' + ',' * 64 + ' ' + 'foo' + ' ' +
    'realm'
        }
    )

    The issue itself is in the following regular expression:

    rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
                    'realm=(["\']?)([^"\']*)\\2', re.I)

    In particular, the (?:.*,)* portion. Since "." and "," overlap and there
    are nested quantifiers we can cause catastrophic backtracking by repeating
    a comma. Note that since AbstractBasicAuthHandler is vulnerable, then both
    HTTPBasicAuthHandler and ProxyBasicAuthHandler are as well because they
    call http_error_auth_reqed. Building from the HTTP authentication document
    above, this means a server can send a specially crafted header along with
    an HTTP 401 or HTTP 407 and cause a DoS on the client.

    I won't speculate on the severity of the issue too much - you will surely
    understand the impact better than I will. Although, the fact that this is
    client-side as opposed to server-side appears to reduce the severity,
    however the fact that it's a security-sensitive context (HTTP
    authentication) may raise the severity.

    One possible fix would be changing the rx expression to the following:

    rx = re.compile('(?:[^,]*,)*[ \t]*([^ \t]+)[ \t]+'
                    'realm=(["\']?)([^"\']*)\\2', re.I)

    This removes the character overlap in the nested quantifier and thus
    negates the catastrophic backtracking.

    Let me know if you have any questions or what the next steps are from here.
    Thanks for supporting Python security!

    --
    Matt Schwager

    @vstinner vstinner added 3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes stdlib Python modules in the Lib dir type-security A security issue labels Jan 30, 2020
    @vstinner
    Copy link
    Member Author

    I added this vulnerability to the following page to track fixes in all Python supported branches:
    https://python-security.readthedocs.io/vuln/urllib-basic-auth-regex.html

    @vstinner
    Copy link
    Member Author

    CVE-2020-8492 has been assigned to this vulnerability:
    https://cve.mitre.org/cgi-bin/cvename.cgi?name=2020-8492

    @vstinner vstinner changed the title [security] Denial of service in urllib.request.AbstractBasicAuthHandler [security][CVE-2020-8492] Denial of service in urllib.request.AbstractBasicAuthHandler Jan 30, 2020
    @vstinner vstinner changed the title [security] Denial of service in urllib.request.AbstractBasicAuthHandler [security][CVE-2020-8492] Denial of service in urllib.request.AbstractBasicAuthHandler Jan 30, 2020
    @bcaller
    Copy link
    Mannequin

    bcaller mannequin commented Feb 4, 2020

    Isn't this a duplicate of bpo-38826 ?

    @vstinner
    Copy link
    Member Author

    Isn't this a duplicate of bpo-38826 ?

    Oh right. I marked it as a duplicate of this issue.

    @vstinner
    Copy link
    Member Author

    bench_parser.py: Benchmark for AbstractBasicAuthHandler.http_error_auth_reqed().

    @bcaller
    Copy link
    Mannequin

    bcaller mannequin commented Apr 2, 2020

    Instead of

    repeat_10_3 = 'Basic ' + ', ' * (10 ** 3) + simple

    in the benchmark, try

    repeat_10_3 = 'Basic ' + ', ' * (10 ** 3) + 'A'

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 2, 2020

    Ooooh, I see. I didn't measure the performance of the right header. I re-run a benchmark using the HTTP header (repeat=15):

        header = 'Basic ' + ', ' * 15 + 'A'

    Now I see a major performance difference. Comparison between master ("ref") and PR 18284 ("fix"):

    Mean +- std dev: [ref] 88.9 ms +- 2.4 ms -> [fix] 17.5 us +- 0.7 us: 5083.23x faster (-100%)

    So the worst case is now way faster: more than 5000x faster!

    It's even possible to go up to repeat=10**6 characters, it still takes less than 1 seconds: 412 ms +- 19 ms.

    On the master branch, repeat=20 already takes around 3 seconds... The slowdown is exponential with repeat increase.

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 2, 2020

    New changeset 0b297d4 by Victor Stinner in branch 'master':
    bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284)
    0b297d4

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 2, 2020

    New changeset ea9e240 by Miss Islington (bot) in branch '3.8':
    bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) (GH-19296)
    ea9e240

    @vstinner
    Copy link
    Member Author

    vstinner commented Apr 2, 2020

    New changeset b57a736 by Miss Islington (bot) in branch '3.7':
    bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) (GH-19297)
    b57a736

    @ned-deily
    Copy link
    Member

    New changeset 69cdeeb by Victor Stinner in branch '3.6':
    bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) (GH-19304)
    69cdeeb

    @larryhastings
    Copy link
    Contributor

    New changeset 37fe316 by Victor Stinner in branch '3.5':
    bpo-39503: CVE-2020-8492: Fix AbstractBasicAuthHandler (GH-18284) (bpo-19305)
    37fe316

    @sxt1001
    Copy link
    Mannequin

    sxt1001 mannequin commented Sep 14, 2021

    headers = headers.get_all(authreq)

    "headers" is a dict object? If so, the dict object does not seem to have no attribute "get_all".

    @sxt1001 sxt1001 mannequin added 3.10 only security fixes 3.11 only security fixes labels Sep 14, 2021
    @sxt1001 sxt1001 mannequin added 3.10 only security fixes 3.11 only security fixes labels Sep 14, 2021
    @vstinner
    Copy link
    Member Author

    "headers" is a dict object? If so, the dict object does not seem to have no attribute "get_all".

    No, it's not a dict object.

    @sxt1001
    Copy link
    Mannequin

    sxt1001 mannequin commented Sep 14, 2021

    At the beginning of the issue, there is the following reproduction code:
    from urllib.request import AbstractBasicAuthHandler
    auth_handler = AbstractBasicAuthHandler()
    auth_handler.http_error_auth_reqed(
    'www-authenticate',
    'unused',
    'unused',
    {
    'www-authenticate': 'Basic ' + ',' * 64 + ' ' + 'foo' + ' ' +
    'realm'
    }
    )

    Here's the headers:

    {
    'www-authenticate': 'Basic ' + ',' * 64 + ' ' + 'foo' + ' ' +
    'realm'
    }

    I think this is a dict object, so the current problem is fixed and no longer compatible with the previous usage?

    @vstinner
    Copy link
    Member Author

    This issue was a security vulnerability. It's now closed, please don't comment closed issues. If you consider that there is a regression, please open a new issue.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes stdlib Python modules in the Lib dir type-security A security issue
    Projects
    None yet
    Development

    No branches or pull requests

    3 participants