Tornado before 6.3.3 contains an HTTP request smuggling vulnerability due to improper parsing of Content-Length headers accepting non-standard characters. Attackers can send crafted HTTP requests with these characters to bypass proxy validation and smuggle requests when deployed behind certain proxies.
Tornado before 6.4.1 ignores duplicate Transfer-Encoding: chunked headers, treating requests as having no message body and parsing the chunked body as a subsequent request. Attackers can exploit this inconsistency when Tornado is deployed behind proxies to perform HTTP request smuggling, enabling access control bypass, cache poisoning, or connection desynchronization.
Tornado before 6.5.8 contains a memory amplification vulnerability in parsemultipartformdata that splits multipart data before validating the maxparts limit. Attackers can send crafted multipart requests with many parts to create large transient lists, exhausting server memory and causing denial of service.
Tornado versions before 6.5.9 contain an unbounded memory accumulation vulnerability in CurlAsyncHTTPClient that allows remote attackers to cause denial of service by sending a compressed response. Attackers can send a gzip-encoded decompression bomb that accumulates in memory without size limits, causing the application process to be killed by out-of-memory conditions.
Tornado before 6.6 contains a path traversal vulnerability in RequestHandler.staticurl() and getversion() that allows attackers to hash files outside staticpath using ../ sequences. Attackers controlling input passed to staticurl() can obtain SHA-512 hashes of readable files to confirm their existence, or reference /dev/zero to block the event loop indefinitely.
Summary
When Tornado's multipart/form-data parser encounters certain errors, it logs a warning but continues trying to parse the remainder of the data. This allows remote attackers to generate an extremely high volume of logs, constituting a DoS attack. This DoS is compounded by the fact that the logging subsystem is synchronous.
Affected versions
All versions of Tornado prior to 6.5 are affected. The vulnerable parser is enabled by default.
Solution
Upgrade to Tornado version 6.5. In the meantime, risk can be mitigated by blocking Content-Type: multipart/form-data in a proxy.
Summary
Tornado parses application/x-www-form-urlencoded bodies with urllib.parse.parseqs and does not pass maxnumfields. A body made almost entirely of separators produces tens of millions of fields, and the parse happens on the event loop before the handler runs, so a single request stalls the whole server.
Where it is
tornado/escape.py, at HEAD e530031405e2154654dedc4c84d5656b557ea310:
python result = urllib.parse.parseqs( qs, keepblankvalues, strictparsing, encoding="latin1", errors="strict" )
maxnumfields is the parameter CPython added for exactly this, and it is absent.
The path to it is entirely server-side and pre-dispatch. RequestHandler.execute parses the body at tornado/web.py:1821, which reaches HTTPServerRequest.parsebody at tornado/httputil.py:636, and the urlencoded branch of parsebodyarguments calls parseqsbytes at tornado/httputil.py:1030.
The size that reaches it is bounded only by the body cap, which defaults to the stream's maxbuffersize of 104857600 at tornado/iostream.py:239, applied as the request body default at tornado/http1connection.py:136-140. A 100 MB body of separators is around fifty million fields.
Impact
Denial of service against the whole process, not one request. Tornado is single-threaded and the parse is synchronous on the event loop, so every other connection waits. No authentication is needed if any route accepts a form post, which is the normal case.
Suggested fix
Pass a bound:
python result = urllib.parse.parseqs( qs, keepblankvalues, strictparsing, encoding="latin1", errors="strict", maxnumfields=maxnumfields, )
with a conservative default and a way for applications to raise it. CPython raises ValueError when the limit is exceeded, which maps cleanly onto a 400.
Lowering the default body cap for urlencoded specifically would help too, since 100 MB of form fields is not a shape any real client sends.
Why I do not think this is a duplicate
The published tornado advisories cover out-of-bounds access in the C extension, unbounded accumulation of decompressed chunks in AsyncHTTPClient, the Authorization header surviving cross-origin redirects, credential leakage on curl handle reuse, and cookie attribute validation. The decompression one is the nearest in spirit and is on the client side; this is the server parsing a request body. The call is unchanged at HEAD.
Tornado versions before 6.5.9 contain an unbounded memory accumulation vulnerability in CurlAsyncHTTPClient that allows remote attackers to cause denial of service by sending a compressed response. Attackers can send a gzip-encoded decompression bomb that accumulates in memory without size limits, causing the application process to be killed by out-of-memory conditions.
Tornado before 6.5.8 contains an incomplete fix for cookie attribute injection that allows attackers to inject arbitrary cookie attributes by passing capitalized or legacy keyword arguments to setcookie. Attackers can embed semicolon-delimited data in capitalized parameters like Domain, Path, or SameSite to bypass validation and modify cookie security attributes.
Open redirect vulnerability in Tornado versions 6.3.1 and earlier allows a remote unauthenticated attacker to redirect a user to an arbitrary web site and conduct a phishing attack by having user access a specially crafted URL.
https://jvn.jp/en/jp/JVN45127776/ https://github.com/tornadoweb/tornado/releases/tag/v6.3.2
Tornado before 6.6 does not validate the maxage argument of RequestHandler.setcookie (also called by setsignedcookie, setsecurecookie, and clearcookie), allowing injection of additional Set-Cookie attributes. When an application passes attacker-influenced input as maxage, an attacker can embed attributes such as Domain, Path, or Secure to widen the cookie's scope to parent paths or other subdomains.