CVE-2026-91990 Overview
CVE-2026-91990 is a memory amplification vulnerability in the Tornado Python web framework before version 6.5.8. The flaw exists in the parse_multipart_form_data function, which splits incoming multipart data before validating the max_parts limit. An unauthenticated attacker can send a crafted multipart request containing many parts, forcing Tornado to allocate large transient lists in memory. The resulting memory exhaustion causes a denial of service (DoS) against the target application. The issue is classified under [CWE-770: Allocation of Resources Without Limits or Throttling].
Critical Impact
Unauthenticated remote attackers can exhaust server memory and crash Tornado-based web applications with a single crafted HTTP multipart request.
Affected Products
- Tornado web framework versions prior to 6.5.8
- Python applications embedding vulnerable Tornado releases
- HTTP services exposing endpoints that accept multipart/form-data uploads
Discovery Timeline
- 2026-09-15 - CVE-2026-91990 published to NVD
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-91990
Vulnerability Analysis
The vulnerability lives in Tornado's HTTP request parsing pipeline, specifically in parse_multipart_form_data. Tornado enforces a max_parts limit intended to bound how many parts a client can submit in a single multipart request. The check is performed after the raw multipart body has already been split into a list of parts. An attacker who submits a body containing millions of boundary occurrences forces the parser to materialize a very large intermediate list before the limit check rejects the request. Memory usage grows proportionally to the number of boundary tokens in the request body, not to the number of parts allowed by policy. Repeated or concurrent requests amplify the effect and can drive the process into out-of-memory (OOM) termination.
Root Cause
The root cause is an ordering flaw: input decomposition occurs before resource-limit enforcement. Because the split operation itself is unbounded, the max_parts guard never gets to protect the allocation path it was intended to protect. This is a canonical [CWE-770] resource allocation issue.
Attack Vector
Exploitation requires only network access to a Tornado endpoint that accepts multipart form submissions. No authentication, user interaction, or prior foothold is required. The attacker sends an HTTP POST with Content-Type: multipart/form-data and a body containing a very high number of boundary delimiters. Tornado's parser splits the body into an oversized list of segments, allocating memory well beyond expected request-processing budgets. See the GitHub Security Advisory GHSA-8423 and the VulnCheck Memory Amplification Advisory for further technical detail.
// No verified proof-of-concept code is published.
// Refer to the linked advisories for reproduction details.
Detection Methods for CVE-2026-91990
Indicators of Compromise
- Sudden spikes in resident memory usage of Tornado worker processes correlated with inbound multipart POST requests.
- Process termination events attributable to the Linux OOM killer targeting Tornado application processes.
- HTTP requests carrying Content-Type: multipart/form-data with abnormally large Content-Length values or unusually high boundary counts.
Detection Strategies
- Inspect web server and reverse-proxy logs for multipart POST bodies whose size or part count deviates from application norms.
- Deploy a web application firewall (WAF) rule that counts boundary occurrences in request bodies and blocks requests exceeding a safe threshold.
- Alert on repeated 5xx responses or worker restarts from Tornado application tiers following multipart uploads.
Monitoring Recommendations
- Track per-process memory metrics (RSS, page faults) on Tornado hosts with alerting on sharp growth events.
- Enable request-body size and part-count metrics at the load balancer or ingress layer.
- Correlate application crash and restart events with upstream client IP addresses to identify abuse sources.
How to Mitigate CVE-2026-91990
Immediate Actions Required
- Upgrade Tornado to version 6.5.8 or later across all Python environments.
- Inventory internal and external services that depend on Tornado, including transitive dependencies pulled in by frameworks and tools.
- Enforce request body size limits at the reverse proxy or WAF for endpoints that accept multipart uploads.
- Rate-limit unauthenticated multipart POST traffic to reduce amplification potential.
Patch Information
The issue is fixed in Tornado 6.5.8. The fix reorders the multipart parsing logic so the max_parts limit is enforced before the body is fully split into part objects, preventing unbounded intermediate allocations. Consult the GitHub Security Advisory GHSA-8423 for the authoritative patch reference.
Workarounds
- Place Tornado behind a reverse proxy that caps request body size (for example, client_max_body_size in NGINX) to a value appropriate for legitimate uploads.
- Terminate multipart parsing at a WAF or API gateway that validates part counts before forwarding to Tornado.
- Constrain per-process memory using cgroups or container memory limits so a single abusive request cannot destabilize the host.
# NGINX reverse proxy example: cap multipart body size upstream of Tornado
http {
client_max_body_size 10m;
client_body_buffer_size 128k;
server {
listen 443 ssl;
location / {
proxy_pass http://tornado_backend;
}
}
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.