VAITP Dataset

← Back to the dataset

CVE-2026-50269

AIOHTTP is vulnerable to header injection via multipart/payload headers.

  • CVSS 2.7
  • CWE-93
  • Input Validation and Sanitization
  • Remote

AIOHTTP is an asynchronous HTTP client/server framework for asyncio and Python. Prior to 3.14.0, attacker-controlled input included into multipart/payload headers can be used to modify a request to inject additional headers or similar. In the unlikely situation that an application is passing user-controlled strings into MultipartWriter.append(headers=…) or Payload.headers, then an attacker may be able to modify the request to inject headers or change the contents of the request. This vulnerability is fixed in 3.14.0.

CVSS base score
2.7
Published
2026-06-22
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Privilege Escalation
Affected component
AIOHTTP
Fixed by upgrading
Yes

Solution

Upgrade aiohttp to version 3.14.0 or later.

Vulnerable code sample

import asyncio
import aiohttp
import io

# This code requires a vulnerable version of aiohttp (e.g., prior to 3.9.2 for
# a similar vulnerability, CVE-2024-23334). On patched versions, this will
# raise a ValueError because of the CRLF characters in the header value.

async def demonstrate_vulnerability():
    # An attacker-controlled string containing CRLF characters to inject a new header.
    malicious_header_value = "form-data; name=field1\r\nInjected-Header: MaliciousValue"

    # A vulnerable application constructs a Payload using the attacker's string
    # directly in the headers.
    payload = aiohttp.Payload(
        b'some-content',
        headers={'Content-Disposition': malicious_header_value}
    )

    # The payload is then added to a multipart writer.
    writer = aiohttp.MultipartWriter('form-data')
    writer.append_payload(payload)

    # To show the result, we serialize the writer's output to a buffer and print it.
    # This simulates what would be sent over the network.
    class RequestBodySerializer:
        def __init__(self):
            self.buffer = io.BytesIO()
        async def write(self, data):
            self.buffer.write(data)
        async def drain(self):
            pass

    serializer = RequestBodySerializer()
    await writer.write(serializer)

    print(serializer.buffer.getvalue().decode())


if __name__ == "__main__":
    try:
        asyncio.run(demonstrate_vulnerability())
    except ValueError as e:
        print(f"Vulnerability blocked on patched aiohttp version:\n{e}")

Patched code sample

import re
from typing import Any, Mapping, Optional

# This regex is representative of the fix, used to find forbidden carriage
# return or line feed characters in header keys and values.
_FORBIDDEN_HEADER_CHARS_RE = re.compile(r"[\r\n]")


def validate_multipart_headers(headers: Optional[Mapping[str, Any]]) -> None:
    """
    This function represents the fix for the header injection vulnerability.

    It validates that header keys and values do not contain carriage return
    (\r) or newline (\n) characters before they are used in a multipart
    request part. The vulnerable version of the code would proceed without
    this check, allowing a specially crafted header value to inject additional
    headers into the final request.
    """
    if headers is None:
        return

    for key, value in headers.items():
        # The fix is to explicitly check both the key and the value for
        # forbidden characters and raise an error if they are found.
        if _FORBIDDEN_HEADER_CHARS_RE.search(str(key)):
            raise ValueError(f"Invalid character in header key: {key!r}")
        if _FORBIDDEN_HEADER_CHARS_RE.search(str(value)):
            raise ValueError(f"Invalid character in header value: {value!r}")


# --- Demonstration of the fix ---

# 1. Define a malicious header value attempting to inject a new header.
# The `\r\n` is an attempt to terminate the current header and start a new one.
malicious_header_value = (
    'form-data; name="file"\r\nInjected-Header: Malicious-Value'
)
malicious_headers = {"Content-Disposition": malicious_header_value}

# 2. The patched code successfully raises a ValueError, preventing the injection.
try:
    validate_multipart_headers(malicious_headers)
    # This line should not be reached
    print("Validation failed to catch malicious header.")
except ValueError as e:
    # This block is expected to execute, demonstrating the fix works.
    print(f"Fix successfully caught malicious header: {e}")
    assert "Invalid character in header value" in str(e)


# 3. A valid header dictionary that passes the check.
valid_headers = {"Content-Disposition": 'form-data; name="file"'}
try:
    validate_multipart_headers(valid_headers)
    print("Validation correctly passed for legitimate header.")
except ValueError:
    # This block should not be reached
    print("Validation incorrectly failed for legitimate header.")

Payload

import aiohttp

# Attacker-controlled string with CRLF injection
malicious_filename = 'pwned.txt"\r\nInjected-Header: Injected-Value'

# This simulates a vulnerable application creating a multipart request part.
# The user-controlled `malicious_filename` is unsafely embedded in a header.
with aiohttp.MultipartWriter('form-data') as mpwriter:
    mpwriter.append(
        b'some file content',
        {
            'Content-Disposition': f'form-data; name="file"; filename="{malicious_filename}"'
        }
    )

Cite this entry

@misc{vaitp:cve202650269,
  title        = {{AIOHTTP is vulnerable to header injection via multipart/payload headers.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-50269},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-50269/}}
}
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 ::