CVE-2026-40347
Python-Multipart DoS via large preamble/epilogue in multipart requests.
- CVSS 5.3
- CWE-400
- Resource Management
- Remote
Python-Multipart is a streaming multipart parser for Python. Versions prior to 0.0.26 have a denial of service vulnerability when parsing crafted `multipart/form-data` requests with large preamble or epilogue sections. Upgrade to version 0.0.26 or later, which skips ahead to the next boundary candidate when processing leading CR/LF data and immediately discards epilogue data after the closing boundary.
- CWE
- CWE-400
- CVSS base score
- 5.3
- Published
- 2026-04-18
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Algorithm
- Code defect classification
- Incorrect Algorithm
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- Python-Multi
- Fixed by upgrading
- Yes
Solution
Upgrade python-multipart to version 0.0.26 or later.
Vulnerable code sample
import io
def vulnerable_read_preamble(stream: io.BytesIO, boundary: bytes):
"""
This function simulates the vulnerable logic before the fix. It reads
all data before the first boundary (the "preamble") into an in-memory
buffer.
If an attacker provides a request body with a very large preamble
(e.g., 1 GB of newline characters), this function will attempt to
buffer all of that data in RAM, leading to excessive memory
consumption and a Denial of Service (DoS).
"""
buffer = bytearray()
boundary_line = b'--' + boundary
while True:
line = stream.readline()
if not line:
# End of stream reached without finding a boundary.
break
if line.startswith(boundary_line):
# Boundary found. The preamble processing is complete.
# A real parser would now handle this boundary line.
break
else:
# VULNERABLE STEP: Append preamble line to the buffer.
# There is no limit on the size of this buffer.
buffer.extend(line)
return bufferPatched code sample
import sys
class FixedMultipartParser:
"""
A conceptual parser demonstrating the fix for a DoS vulnerability
caused by large preambles and epilogues in multipart data.
"""
_STATE_PREAMBLE = 0
_STATE_PART = 1
_STATE_DONE = 2
def __init__(self, boundary: bytes):
self.boundary = b'--' + boundary
self.final_boundary = self.boundary + b'--'
self.state = self._STATE_PREAMBLE
self._buffer = b''
def feed_data(self, data: bytes):
"""
Feeds data to the parser.
The fix is demonstrated by:
1. Immediately stopping processing and discarding data if the final
boundary has been seen (state is _STATE_DONE).
2. Efficiently searching for the first boundary instead of buffering
or slowly processing the entire preamble.
"""
# FIX 1: If parsing is finished, immediately discard any new data (epilogue).
# A vulnerable parser would continue to buffer this, leading to DoS.
if self.state == self._STATE_DONE:
return
self._buffer += data
if self.state == self._STATE_PREAMBLE:
# FIX 2: Efficiently find the first boundary and discard the preamble.
# A vulnerable parser might process the preamble byte-by-byte, consuming
# CPU and memory for large preambles.
try:
boundary_pos = self._buffer.index(self.boundary)
# Discard the preamble before the boundary
self._buffer = self._buffer[boundary_pos:]
self.state = self._STATE_PART
except ValueError:
# To prevent unbounded buffer growth from a huge preamble without a
# boundary, only keep a small tail of the buffer.
tail_size = len(self.boundary) - 1
if len(self._buffer) > tail_size:
self._buffer = self._buffer[-tail_size:]
return
if self.state == self._STATE_PART:
# In a real parser, this would process headers and data between boundaries.
# For this demonstration, we just look for the final boundary.
try:
final_boundary_pos = self._buffer.index(self.final_boundary)
# Final boundary found. Stop processing.
self.state = self._STATE_DONE
# Discard all buffered data, including the final boundary itself.
self._buffer = b''
except ValueError:
# Final boundary not yet in buffer. A real implementation would also
# look for intermediate boundaries to yield completed parts.
passPayload
(b'\r\n' * 10000000) + \
b'--boundary\r\n' \
b'Content-Disposition: form-data; name="field1"\r\n\r\n' \
b'value1\r\n' \
b'--boundary--\r\n'
Cite this entry
@misc{vaitp:cve202640347,
title = {{Python-Multipart DoS via large preamble/epilogue in multipart requests.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-40347},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-40347/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
