CVE-2024-10624
ReDoS in gradio gr.Datetime via regex `^(?:\s*now\s*…)`, causes high CPU usage.
- CVSS 7.5
- CWE-1333
- Input Validation and Sanitization
- Remote
A Regular Expression Denial of Service (ReDoS) vulnerability exists in the gradio-app/gradio repository, affecting the gr.Datetime component. The affected version is git commit 98cbcae. The vulnerability arises from the use of a regular expression `^(?:\s*now\s*(?:-\s*(\d+)\s*([dmhs]))?)?\s*$` to process user input. In Python's default regex engine, this regular expression can take polynomial time to match certain crafted inputs. An attacker can exploit this by sending a crafted HTTP request, causing the gradio process to consume 100% CPU and potentially leading to a Denial of Service (DoS) condition on the server.
- CWE
- CWE-1333
- CVSS base score
- 7.5
- Published
- 2025-03-20
- OWASP
- A03 Injection
- Orthogonal defect classification
- Algorithm
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- Design Defects
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- gradio
- Fixed by upgrading
- Yes
Solution
Upgrade to a version of `gradio` containing a fix for commit 98cbcae, or a later version. Check the Gradio release notes for the specific version number with the fix.
Vulnerable code sample
import re
import time
def vulnerable_datetime_parser(input_string):
"""
Simulates a vulnerable datetime parsing function using the problematic regex.
"""
pattern = r"^(?:\s*now\s*(?:-\s*(\d+)\s*([dmhs]))?)?\s*$"
try:
match = re.match(pattern, input_string)
if match:
print(f"Match found for: {input_string}")
# Simulate some processing time if a match is found. This is to show that
# even a 'successful' match can be slow.
time.sleep(0.001) #Added time.sleep to show slow processing.
return True
else:
print(f"No match found for: {input_string}")
return False
except re.error as e:
print(f"Regex error: {e}")
return False
if __name__ == "__main__":
# Example of a vulnerable input
vulnerable_input = "now" + " " * 100 + "-" + " " * 100 + "1" + " " * 100 +"d" + " " * 100 #String with many spaces
#Example of another vulnerable input
vulnerable_input_2 = "now" + " " * 20000 #Long string of spaces
start_time = time.time()
vulnerable_datetime_parser(vulnerable_input)
end_time = time.time()
print(f"Processing time for vulnerable input 1: {end_time - start_time:.4f} seconds")
start_time = time.time()
vulnerable_datetime_parser(vulnerable_input_2)
end_time = time.time()
print(f"Processing time for vulnerable input 2: {end_time - start_time:.4f} seconds")Patched code sample
import re
def sanitize_datetime_input(user_input):
"""
Sanitizes datetime input to prevent ReDoS.
This function replaces the vulnerable regex with a safe and efficient method
for validating and extracting information from user input. Instead of
complex backtracking, it uses a combination of simple regex patterns and
string splitting to achieve the desired functionality. It now also uses
a maximum input length to mitigate DoS attempts.
Args:
user_input: The user-provided datetime string.
Returns:
A tuple containing (valid, offset_value, offset_unit) where:
valid: True if the input is valid, False otherwise.
offset_value: The numeric offset value (int) if present, None otherwise.
offset_unit: The offset unit (str) if present, None otherwise.
"""
MAX_INPUT_LENGTH = 50 # Prevent excessively long inputs
if len(user_input) > MAX_INPUT_LENGTH:
return False, None, None
# Basic check for "now"
user_input = user_input.strip()
if not user_input.startswith("now") and user_input != "":
return False, None, None
#If input is exactly "now", no further parsing needed
if user_input == "now":
return True, None, None
# Check for "now - ..." pattern
parts = user_input.split("-")
if len(parts) > 2:
return False, None, None
if len(parts) == 1:
return True, None, None #The case where the user input is only "now". Already handled previously, but left to make things very clear.
# Extract offset part
offset_part = parts[1].strip()
# Extract value and unit using a simple regex
match = re.match(r"(\d+)([dmhs])", offset_part)
if not match:
return False, None, None
offset_value = int(match.group(1))
offset_unit = match.group(2)
return True, offset_value, offset_unit
# Example usage demonstrating fix against ReDoS
#Vulnerable Pattern: `^(?:\s*now\s*(?:-\s*(\d+)\s*([dmhs]))?)?\s*$`
#Test cases:
#This example shows a ReDoS exploitation attempt using a long sequence of spaces interspersed with "now" and "-". The original regex used backtracking to process this long string of data, which can take polynomial time to match, leading to a Denial of Service (DoS) condition on the server by consuming 100% CPU.
user_input_vuln = "now" + " " * 50 + "now" + " " * 50 + "now" + " " * 50 + "now" + " " * 50 + "now" #Example of a long string that triggers catastrophic backtracking on the vulnerable Regex
is_valid, offset, unit = sanitize_datetime_input(user_input_vuln)
print(f"Input: {user_input_vuln}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}")
user_input_valid = "now - 10d" #Example of a valid user input
is_valid, offset, unit = sanitize_datetime_input(user_input_valid)
print(f"Input: {user_input_valid}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}")
user_input_invalid = "now - abc" #Example of an invalid user input
is_valid, offset, unit = sanitize_datetime_input(user_input_invalid)
print(f"Input: {user_input_invalid}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}")
user_input_now = "now" #Example of a user input of just "now"
is_valid, offset, unit = sanitize_datetime_input(user_input_now)
print(f"Input: {user_input_now}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}")
user_input_empty = "" #Example of an empty user input
is_valid, offset, unit = sanitize_datetime_input(user_input_empty)
print(f"Input: {user_input_empty}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}")
user_input_long = "now - 10d" * 10 #Example of a long user input string
is_valid, offset, unit = sanitize_datetime_input(user_input_long)
print(f"Input: {user_input_long}, Valid: {is_valid}, Offset: {offset}, Unit: {unit}") # This will return invalid because of max input length check.Payload
`"now - 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 d"`
Cite this entry
@misc{vaitp:cve202410624,
title = {{ReDoS in gradio gr.Datetime via regex `^(?:\s*now\s*...)`, causes high CPU usage.
}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2024-10624},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2024-10624/}}
}
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 ::
