HTTP header splitting in gunicorn 19.4.5

Timeline:
  • 02 Apr 2018: This blog post is published
  • 02 Apr 2018: CVE ID requested
  • 06 Apr 2018: CVE-2018-1000164 assigned

During a vulnerability research spree, I came across this GitHub issue titled Potential HTTP Response Splitting Vulnerability, belonging to the gunicorn project. The title says “potential”, but the vulnerability was present and got fixed in commit 6c3d8.

Unfortunately, this vulnerability hasn’t been reported by anyone to MITRE nor to the Distributed Weakness Filing (DWF) System; therefore it’s not listed in any public CVE database.

In an effort to spread this information to anyone considering using this version of gunicorn, I’ll fill in a DWF report hoping this issue gets a CVE ID and that knowledge of its existance becomes more widespread.

An HTTP header splitting vulnerability is caused by not sanitizing strings containing characters with special meaning in HTTP (such as CR and LF) in data that will later be used to generate HTTP headers.

We can test this vulnerability by creating a Python2 virtual environment with gunicorn 19.4.5 installed:

user@pc:~$ virtualenv venv
user@pc:~$ source venv/bin/activate
(venv) user@pc:~$ pip install gunicorn==19.4.5

The following code (myapp.py) will define both Foo and Bar:

def app(environ, start_response):
    data = b"Hello, World!\n"
    start_response("200 OK", [
        ("Content-Type", "text/plain"),
        ("Foo", "Foo\r\nBar: Bar"),
        ("Content-Length", str(len(data)))
    ])
    return iter([data])

We can run this by executing gunicorn -w 4 myapp:app and going to http://127.0.0.1:8000. Here’s the resulting HTTP response:

user@pc:~$ curl -i http://127.0.0.1:8000/
HTTP/1.1 200 OK
Server: gunicorn/19.4.5
Connection: close
Content-Type: text/plain
Foo: Foo
Bar: Bar
Content-Length: 14

Hello, World!

If we attempt to do this in gunicorn 19.5.0+, this will be the resulting HTTP response:

user@pc:~$ curl -i http://127.0.0.1:8000/
HTTP/1.1 400 Bad Request
Connection: close
Content-Type: text/html
Content-Length: 163

<html>
  <head>
    <title>Bad Request</title>
  </head>
  <body>
    <h1><p>Bad Request</p></h1>
    Invalid HTTP Header: "'Foo\\r\\nBar: Bar'"
  </body>
</html>

This behavior is expected, thanks to commit 6c3d8.