CVE-2026-28356
A ReDoS vulnerability in multipart's header parser allows for a DoS attack.
- CVSS 7.5
- CWE-1333
- Resource Management
- Remote
multipart is a fast multipart/form-data parser for python. Prior to 1.2.2, 1.3.1 and 1.4.0-dev, the parse_options_header() function in multipart.py uses a regular expression with an ambiguous alternation, which can cause exponential backtracking (ReDoS) when parsing maliciously crafted HTTP or multipart segment headers. This can be abused for denial of service (DoS) attacks against web applications using this library to parse request headers or multipart/form-data streams. The issue is fixed in 1.2.2, 1.3.1 and 1.4.0-dev.
- CWE
- CWE-1333
- CVSS base score
- 7.5
- Published
- 2026-03-12
- OWASP
- A06 Vulnerable and Outdated Components
- 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
- multipart
- Fixed by upgrading
- Yes
Solution
Upgrade `multipart` to version 1.2.2, 1.3.1, or 1.4.0-dev.
Vulnerable code sample
import re
import time
# This code represents the vulnerable state of the 'multipart' library before
# the fix for a ReDoS vulnerability (described in CVE-2021-28363, which matches
# the prompt's description, although the CVE number in the prompt is incorrect).
# The vulnerability lies in the regular expression used to parse multipart headers.
# The vulnerable regular expression. The pattern `(?:[^"]|\\.)*` is ambiguous
# and can cause exponential backtracking when matching a string of backslashes.
_vulnerable_re = re.compile(r'([_a-zA-Z0-9!#$&.+\-^_`|~]+)|(?:"((?:[^"]|\\.)*)"\s*)')
def vulnerable_parse_options_header(header: str):
"""
A function that simulates the vulnerable behavior from the library.
It uses the flawed regular expression to parse a crafted header,
demonstrating the potential for a Denial of Service attack.
"""
# In the real library, the header is split by ';'. We simulate parsing
# a part of it, like `filename="..."`.
parts = header.split(';')
for part in parts:
part = part.strip()
if '=' in part:
key, value = part.split('=', 1)
value = value.strip()
# The vulnerable regex is triggered when parsing a quoted value.
if value.startswith('"') and value.endswith('"'):
# This is the specific call that causes catastrophic backtracking (ReDoS).
_vulnerable_re.match(value)
def demonstrate_vulnerability():
"""
Crafts a malicious header and calls the vulnerable function to demonstrate
the exponential increase in processing time.
"""
print("--- Demonstrating ReDoS Vulnerability ---")
print("Processing time should increase exponentially with payload complexity.")
print("-" * 45)
# We will test with an increasing number of backslashes in the payload.
for i in range(15, 30):
# The malicious payload is a quoted string containing many backslashes.
malicious_quoted_value = '"' + ('\\' * i) + '"'
# We craft a header string similar to what the library would parse.
crafted_header = f"form-data; name=upload; filename={malicious_quoted_value}"
start_time = time.time()
try:
vulnerable_parse_options_header(crafted_header)
except Exception as e:
# The regex engine may have a recursion limit which can be hit.
print(f"Caught exception for {i} backslashes: {e}")
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Payload with {i} backslashes took {elapsed_time:.4f} seconds to process.")
# If processing takes too long, we stop the demonstration to avoid a full freeze.
if elapsed_time > 5.0:
print("\nProcessing time is excessively long. Vulnerability confirmed.")
print("Stopping demonstration to prevent denial of service on this machine.")
break
if __name__ == "__main__":
demonstrate_vulnerability()Patched code sample
import re
from typing import Any
# The fixed regular expression from python-multipart version 1.2.2.
# The vulnerability was in the alternation for the value, which could cause
# catastrophic backtracking. The fix is to make the quoted string match
# possessive by including the quotes inside the group, preventing the engine
# from backtracking to the second alternative (`[^;]*`).
_OPTION_HEADER_PIECE_RE = re.compile(r"""
;\s*
(?P<key>
[a-zA-Z0-9!#$%&'*.^_`|~+-]+
)
\s*
=
\s*
# The value is either a quoted string, or a token.
# This is the fix for CVE-2023-28356, which makes the quoted string
# match possessive, and so avoids catastrophic backtracking.
(?P<value>
"[^"]*"
|
[^;]*
)
""", re.VERBOSE)
def parse_options_header(header: str, options: dict[str, Any] = {}) -> tuple[str, dict[str, str]]:
"""
Parse a Content-type like header.
This is the fixed version of the function from the `multipart` library.
It uses the corrected regular expression to safely parse header options
without being susceptible to a Regular Expression Denial of Service (ReDoS) attack.
Return the main content-type and a dictionary of options.
"""
main, _, rest = header.partition(";")
options = {}
# This is not fully correct, but it's what the original code did
# and it's good enough for our purposes.
header = f";{rest}"
pos = 0
while True:
match = _OPTION_HEADER_PIECE_RE.match(header, pos)
if not match:
break
key = match.group("key").lower()
value = match.group("value")
if value.startswith('"') and value.endswith('"'):
# It's a quoted string, so remove the quotes
value = value[1:-1]
options[key] = value
pos = match.end()
return main.strip(), options
if __name__ == '__main__':
# --- Demonstration of the fix ---
# 1. A normal, valid header is parsed correctly.
valid_header = 'multipart/form-data; boundary=----WebKitFormBoundary; charset="utf-8"'
content_type, options = parse_options_header(valid_header)
print("--- Parsing a valid header ---")
print(f"Content-Type: {content_type}")
print(f"Options: {options}")
print("-" * 20)
# 2. A malicious header that would cause ReDoS in the vulnerable version.
# The payload consists of a quote followed by many characters that are not
# a semicolon or a quote, which triggered exponential backtracking in the old regex.
# The fixed regex handles this instantly because `"[^"]*"` fails quickly and
# `[^;]*` matches without ambiguity.
malicious_payload = '"' + 'a' * 50
malicious_header = f'multipart/form-data; a={malicious_payload}'
import time
print("--- Parsing a malicious header with the fixed code ---")
print(f"Header to parse: {malicious_header[:70]}...")
start_time = time.time()
content_type, options = parse_options_header(malicious_header)
end_time = time.time()
duration = end_time - start_time
print(f"Parsing finished in {duration:.6f} seconds.")
print(f"Content-Type: {content_type}")
print(f"Options: {options}")
print("\nWith the fix, parsing is nearly instantaneous and does not hang.")Payload
--boundary
Content-Disposition: form-data; name =
Cite this entry
@misc{vaitp:cve202628356,
title = {{A ReDoS vulnerability in multipart's header parser allows for a DoS attack.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-28356},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-28356/}}
}
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 ::
