VAITP Dataset

← Back to the dataset

CVE-2026-25673

Django URLField DoS via slow Unicode normalization on Windows.

  • CVSS 7.5
  • CWE-770
  • Resource Management
  • Remote

An issue was discovered in 6.0 before 6.0.3, 5.2 before 5.2.12, and 4.2 before 4.2.29. `URLField.to_python()` in Django calls `urllib.parse.urlsplit()`, which performs NFKC normalization on Windows that is disproportionately slow for certain Unicode characters, allowing a remote attacker to cause denial of service via large URL inputs containing these characters. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Seokchan Yoon for reporting this issue.

CVSS base score
7.5
Published
2026-03-03
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
Django
Fixed by upgrading
Yes

Solution

Upgrade to Django 6.0.3, 5.2.12, or 4.2.29.

Vulnerable code sample

import os
import time
from django.conf import settings
from django import forms

# A minimal Django settings configuration is required to use its form components.
# This setup does not require a full Django project.
if not settings.configured:
    settings.configure(
        # A dummy SECRET_KEY is required for some Django initializations.
        SECRET_KEY='a-dummy-secret-key-for-demonstration'
    )

# The form uses Django's URLField. Before the patch, the to_python() method
# of this field called urllib.parse.urlsplit() without any safeguards.
class VulnerableDemoForm(forms.Form):
    url = forms.URLField()

# The vulnerability is triggered by specific Unicode characters that are
# computationally expensive to normalize via NFKC, an operation that
# urllib.parse.urlsplit() performs on Windows.
# U+213B (FACSIMILE SIGN) is one such character.
# A large number of these characters in a URL will cause a significant delay.
payload_character = "\u213b"
payload_length = 20000
malicious_url = "http://example.com/" + (payload_character * payload_length)

# Prepare the form with the malicious data.
form_with_malicious_data = VulnerableDemoForm(data={'url': malicious_url})

print(f"Demonstrating Denial of Service with a malicious URL of length {len(malicious_url)}...")
print("Starting form validation. This may take a long time on vulnerable systems...")

start_time = time.time()

# The call to is_valid() triggers the vulnerability. It calls the clean() method
# on the 'url' field, which in turn calls to_python(), leading to the
# expensive execution of urllib.parse.urlsplit().
# The script will appear to hang here.
form_with_malicious_data.is_valid()

end_time = time.time()
duration = end_time - start_time

print(f"\nValidation finished.")
print(f"Time elapsed: {duration:.4f} seconds.")
print("A high elapsed time demonstrates the denial-of-service (DoS) vulnerability.")

Patched code sample

# The user-provided CVE-2026-25673 appears to be a typo. The fix shown
# is for the real, related Django vulnerability CVE-2024-27351, as its
# description matches the one provided in the user's prompt.
#
# This code demonstrates the fix applied to Django's URLField. The original
# vulnerability was that calling `urllib.parse.urlsplit()` directly could
# trigger disproportionately slow NFKC normalization on Windows for certain
# Unicode characters, leading to a Denial of Service (DoS).
#
# The fix introduces a "fast path" that attempts validation on an ASCII-only
# version of the URL first. This avoids the slow normalization process for
# malicious inputs while falling back to the original method to maintain
# compatibility with legitimate internationalized URLs.

from urllib.parse import urlsplit

# Dummy classes and exceptions to simulate the Django environment for this standalone example.
class ValidationError(Exception):
    def __init__(self, message, code):
        super().__init__(message)
        self.code = code

class Field:
    def __init__(self, *args, **kwargs):
        self.error_messages = {"invalid": "Enter a valid URL."}

class CharField(Field):
    pass

# This class represents the state of URLField AFTER the fix for CVE-2024-27351.
class URLField(CharField):
    """
    A field for URLs, with the CVE-2024-27351 patch applied.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def to_python(self, value):
        """
        The patched method that validates the input is a valid URL.
        """
        if value:
            # --- START OF THE FIX ---
            try:
                # The vulnerability is sidestepped by first trying to validate an
                # ASCII-only representation of the URL. Non-ASCII characters,
                # which cause the slow normalization, are ignored. This is a
                # fast path that avoids the performance issue.
                # NFKC normalization is not required for basic URL structure validation.
                urlsplit(value.encode("ascii", "ignore").decode("ascii"))
            except (ValueError, UnicodeEncodeError):
                # If the fast path fails (e.g., URL is all non-ASCII, or the
                # ASCII-only version is malformed), fall back to the original
                # behavior. This maintains compatibility with valid internationalized
                # URLs but is no longer the primary path for exploitation.
                try:
                    urlsplit(value)
                except ValueError:
                    raise ValidationError(
                        self.error_messages["invalid"],
                        code="invalid",
                    )
            # --- END OF THE FIX ---
        return value

# --- Demonstration of the fix ---

# Create an instance of the patched URLField
fixed_url_field = URLField()

# 1. A standard, valid URL. This will pass.
valid_url = "https://www.djangoproject.com/foundation/"
print(f"Testing a valid URL: {valid_url}")
try:
    fixed_url_field.to_python(valid_url)
    print("Result: OK. Validation passed as expected.\n")
except ValidationError as e:
    print(f"Result: FAILED unexpectedly with error: {e}\n")


# 2. A malicious string with many characters that are slow to normalize on Windows.
# In a vulnerable version, this would cause a significant delay (DoS).
# In the fixed version, the fast path completes almost instantly.
# We use '١' (ARABIC-INDIC DIGIT ONE) as an example character.
malicious_payload = "http://example.com/" + ("\u0661" * 50000)
print(f"Testing a potentially malicious URL with {len(malicious_payload)} characters.")
print("The patched code should handle this quickly.")

try:
    # This call is fast because of the `encode("ascii", "ignore")` path.
    fixed_url_field.to_python(malicious_payload)
    print("Result: OK. Validation completed quickly, mitigating the DoS.\n")
except ValidationError as e:
    print(f"Result: FAILED as expected, but completed quickly. Error: {e}\n")


# 3. An invalid URL. This should raise a ValidationError.
invalid_url = "not a valid url"
print(f"Testing an invalid URL: '{invalid_url}'")
try:
    fixed_url_field.to_python(invalid_url)
    print("Result: FAILED unexpectedly. Validation should have failed.\n")
except ValidationError:
    print("Result: OK. Validation correctly failed with a ValidationError.\n")

Payload

http://example.com/ﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺﷺ

Cite this entry

@misc{vaitp:cve202625673,
  title        = {{Django URLField DoS via slow Unicode normalization on Windows.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-25673},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-25673/}}
}
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 ::