CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-3HV7-MJH2-FV65

GHSA-3HV7-MJH2-FV65: Unbounded Query-String Parsing Denial of Service in Tornado Web Server

Amit Schendel
Amit Schendel
Senior Security Researcher

Oct 1, 2026·6 min read·5 visits

Executive Summary (TL;DR)

A design asymmetry in Tornado's query-string parser allows unbounded key-value field processing on GET requests, allowing remote attackers to starve CPU resources and block the event loop, causing a denial of service.

An uncontrolled resource consumption vulnerability in Tornado's HTTP query-string parser allows remote, unauthenticated attackers to trigger CPU exhaustion and block the single-threaded event loop via crafted request URIs containing large numbers of parameters.

Vulnerability Overview

The Tornado web server is an asynchronous networking library and web framework built to run on a single-threaded event loop known as the IOLoop. In an event-loop-driven system, synchronous, CPU-bound tasks block the execution of all other concurrent connections. If any system component executes an expensive synchronous operation, the entire application freezes for the duration of that task.

To mitigate CPU-exhaustion vectors, the Tornado framework introduced restrictions on incoming payloads. In version 6.5.8, a restriction was added to limit the number of fields in URL-encoded POST bodies to 1,000 fields using the max_num_fields parameter. This effectively protected the application against body-based multi-field exploitation.

However, the HTTP GET query-string parsing logic was not restricted. Because query strings bypass the POST-body parser and funnel directly to the initial request initialization parser, this path remained entirely unbounded. Unauthenticated remote attackers can leverage this unconstrained execution path to send requests with thousands of query-string parameters.

Root Cause Analysis

The core of the vulnerability lies in the asymmetrical implementation of input validation between the POST body parser and the query-string parser inside tornado/httputil.py. While the POST path calls parse_body_arguments with an explicit max_num_fields argument, the GET path initializes arguments within HTTPServerRequest.__init__ using an unconstrained call.

Under the hood, Tornado maps query-string parsing to tornado.escape.parse_qs_bytes, which wraps Python's standard library utility urllib.parse.parse_qsl. Python's standard parser is highly CPU-intensive when handling a massive quantity of fields. The parsing algorithm involves repetitive dictionary lookups, string splitting, and URL-decoding logic that exhibits superlinear execution costs as parameter counts rise.

Although Tornado enforces a global default header limit of 64KB (max_header_size), an attacker can craft highly dense payloads containing thousands of minimal keys. A 64KB payload can pack up to approximately 12,700 individual key-value pairs. This volume is more than sufficient to introduce blocking latency on the single-threaded event loop, which cannot process new events while parsing the query.

Code-Level Analysis and Patch Verification

A direct comparison of the vulnerable and patched file tornado/httputil.py reveals the exact mechanism of the fix. In the unpatched version, the request constructor processes the incoming query string without passing any boundary limits.

# Vulnerable implementation in Tornado < 6.5.9
if uri is not None:
    self.path, sep, self.query = uri.partition("?")
self.arguments = parse_qs_bytes(self.query, keep_blank_values=True)

The patched version integrates boundary defense by pulling the global limit from _DEFAULT_PARSE_BODY_CONFIG and wrapping the initialization in a try-except block to gracefully handle exceptions as HTTP 400 errors.

# Patched implementation in Tornado 6.5.9
if uri is not None:
    self.path, sep, self.query = uri.partition("?")
try:
    self.arguments = parse_qs_bytes(
        self.query,
        keep_blank_values=True,
        # The query string is bounded by max_header_size, but parsing
        # is expensive enough per field to be worth limiting. Use the
        # same limit as a urlencoded body. This reads the global
        # config because HTTPServerRequest has no access to the
        # per-connection configuration; see set_parse_body_config.
        max_num_fields=_DEFAULT_PARSE_BODY_CONFIG.urlencoded.max_arguments,
    )
except Exception as e:
    raise HTTPInputError("Invalid query string: %s" % e) from e

While this patch is highly effective for standard deployments, certain limitations exist. If downstream developers manually access the raw self.request.query and pass it to unconstrained third-party parsers, they remain vulnerable to CPU exhaustion. Additionally, because the query string parser lacks direct access to the per-connection configuration, it relies entirely on the global config default, meaning administrators cannot configure customized query-string limits per handler.

Exploitation and Proof-of-Concept Analysis

To execute this denial of service attack, an unauthenticated remote attacker identifies a public interface exposing a standard handler. The attacker then structures a request line with an extremely high concentration of parameters, such as GET /?k0=1&k1=1&k2=1... up to thousands of parameters.

Empirical verification conducted against an unpatched instance shows that standard baseline probes to /?a=1 require approximately 1.86ms of processing latency. When a payload of 7,800 parameters (~61KB) is sent to the server, the parsing processing latency rises to an average of 25.1ms, which represents an increase of over 13 times the baseline duration.

When multiple concurrent requests are initiated, the compounding latency starves the IOLoop completely. In dynamic testing, sending five concurrent exploit requests caused legitimate baseline requests to face an average latency of 13.0ms, with peak latency reaching 118.1ms. If the default max_header_size is increased from 64KB, the execution time scales exponentially, allowing a single request to lock the CPU for several seconds.

Impact Assessment

The security impact of GHSA-3HV7-MJH2-FV65 is classified as a low-to-medium availability threat with a CVSS score of 5.3. The attack can be executed remotely without privileges, bypasses internal authentication layers, and does not require user interaction.

Although the flaw does not lead to remote code execution, information leakage, or memory corruption, its impact on application availability is immediate. Because Tornado is single-threaded, a steady, low-bandwidth stream of high-density requests can degrade performance to a state where standard connections timeout.

Organizations running latency-critical web services, real-time message brokers, or WebSockets on unpatched Tornado versions are vulnerable. Under sustained exploitation, upstream reverse proxies and load balancers may mark the unresponsive Tornado upstream nodes as unhealthy, resulting in complete service dropouts.

Remediation and Mitigation Guidance

The primary recommendation is to update the Tornado framework package to version 6.5.9 or higher. This update restricts query strings to 1,000 arguments and rejects oversized payloads with a 400 Bad Request error.

If upgrading immediately is not an option, you can implement reverse proxy mitigations to shield the upstream server. For instance, in an Nginx deployment, keep the large_client_header_buffers directive strict, or add configuration to reject queries exceeding a specific length:

if ($query_string ~ "^.{8192,}") {
    return 414;
}

Additionally, developers can programmatically limit the maximum permissible header size in the Tornado HTTP server instantiation block, which minimizes the capacity of the vector:

# Restrict maximum header size to 16KB to reduce parameters payload density
http_server = tornado.httpserver.HTTPServer(app, max_header_size=16384)

Fix Analysis (2)

Technical Appendix

CVSS Score
5.3/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Affected Systems

Tornado Web Server

Affected Versions Detail

Product
Affected Versions
Fixed Version
tornado
Tornado
< 6.5.96.5.9
AttributeDetail
CWE IDCWE-400 (Uncontrolled Resource Consumption) / CWE-407 (Algorithmic Complexity)
Attack VectorNetwork (Unauthenticated)
CVSS v3.1 Score5.3 (Medium)
EPSS ScoreNot Applicable (requires CVE ID)
Exploit StatusPoC (Proof-of-Concept)
CISA KEV StatusNot Applicable
ImpactDenial of Service (DoS)

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-400
Uncontrolled Resource Consumption

The system does not adequately limit the allocation of CPU resources when processing multi-field query-string inputs.

Known Exploits & Detection

GitHub Security Advisory GHSA-3hv7-mjh2-fv65Official advisory with detailed reproduction specifications and timeline.

Vulnerability Timeline

Official bug fix committed to Tornado master
2026-09-13
Cherry-picked fix committed to 6.5 release branch
2026-09-15
Tornado v6.5.9 released and GHSA-3HV7-MJH2-FV65 advisory published
2026-09-30
OSV database record updated
2026-10-01

References & Sources

  • [1]GitHub Security Advisory GHSA-3hv7-mjh2-fv65
  • [2]Tornado Fix Pull Request #3719
  • [3]Tornado v6.5.9 Release Tag
  • [4]Tornado Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•43 minutes ago•CVE-2026-93981
4.7

CVE-2026-93981: Cross-Site Scripting via Unescaped SSR Pathways in Hono JSX Engine

An improper neutralization of input during web page generation (CWE-79) vulnerability exists in the server-side rendering JSX engine (hono/jsx) of the Hono web framework prior to version 4.13.7. The flaw enables unauthenticated remote attackers to execute arbitrary JavaScript in the victim's browser context by supplying unescaped HTML characters into user-controlled fields rendered within specific boundary components, context providers, or direct server-side utilities.

Alon Barad
Alon Barad
3 views•6 min read
•about 2 hours ago•GHSA-59CR-6R3X-644W
8.8

GHSA-59CR-6R3X-644W: GitPython Submodule Update Path Traversal Can Write Outside the Repository

A path traversal vulnerability exists in GitPython when handling submodule updates recursively. If an attacker crafts a malicious repository with traversed paths or symbolic links in the submodule configuration, they can execute arbitrary file writes outside the parent repository's working directory. This can lead to system configuration modifications or arbitrary code execution.

Amit Schendel
Amit Schendel
5 views•5 min read
•about 3 hours ago•GHSA-C2M8-H5V5-343R
7.5

GHSA-C2M8-H5V5-343R: Path Traversal via Improper Link Resolution in Tornado StaticFileHandler

A path traversal and arbitrary file disclosure vulnerability exists in Tornado's StaticFileHandler. In versions prior to 6.5.9, the handler follows symbolic links that point outside of the configured root static directory. This behavior occurs because the handler performs lexical path validation rather than physical filesystem resolution, allowing unauthenticated remote attackers to read arbitrary files if they can access or control symbolic links within the served static root.

Amit Schendel
Amit Schendel
5 views•4 min read
•about 4 hours ago•GHSA-CHX6-46F5-W4VP
7.5

GHSA-CHX6-46F5-W4VP: Uncontrolled Resource Consumption in Tornado CurlAsyncHTTPClient

A critical uncontrolled resource consumption vulnerability exists in the Tornado web server's libcurl-based HTTP client (CurlAsyncHTTPClient). When processing highly compressed responses with response decompression enabled, the client experiences unbounded memory growth. This leads to host memory exhaustion and denial of service via application crashes.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 6 hours ago•CVE-2026-103001
6.5

CVE-2026-103001: State Pollution in PyJWT Option Merging Leads to Claim Verification Bypass

PyJWT versions 2.11.0 through 2.13.0 suffer from a state pollution vulnerability in the `_merge_options` method. When an application passes a mutable configuration mapping with signature verification disabled, the library modifies the object in-place. If this same dictionary is reused for subsequent verified decode operations, standard claim verifications (such as expiration, audience, and issuer validation) are silently bypassed.

Amit Schendel
Amit Schendel
7 views•6 min read
•about 7 hours ago•CVE-2026-92081
5.9

CVE-2026-92081: Denial of Service via Uncaught Exception on HTTP/2 Response Trailers in Fastify

A protocol validation vulnerability exists in Fastify before version 5.12.5. When serving requests over HTTP/2, Fastify unconditionally injects the forbidden Transfer-Encoding header when response trailers are used, triggering an uncaught exception in Node.js and crashing the process.

Alon Barad
Alon Barad
4 views•7 min read