VAITP Dataset

← Back to the dataset

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.

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 ::