CVE-2025-68142
ReDoS in PyMdown Extensions' figure caption allows for Denial of Service.
- CVSS 2.7
- CWE-1333
- Resource Management
- Remote
PyMdown Extensions is a set of extensions for the `Python-Markdown` markdown project. Versions prior to 10.16.1 have a ReDOS bug found within the figure caption extension (`pymdownx.blocks.caption`). In systems that take unchecked user content, this could cause long hanges when processing the data if a malicious payload was crafted. This issue is patched in Release 10.16.1. As a workaround, those who process unknown user content without timeouts or other safeguards in place to prevent really large, malicious content being aimed at systems may avoid the use of `pymdownx.blocks.caption` until they're able to upgrade.
- CWE
- CWE-1333
- CVSS base score
- 2.7
- Published
- 2025-12-16
- 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
- PyMdown Exte
- Fixed by upgrading
- Yes
Solution
Upgrade PyMdown Extensions to version 10.16.1 or later.
Vulnerable code sample
import markdown
import re
import time
from markdown.extensions import Extension
from markdown.blockprocessors import BlockProcessor
# This PoC demonstrates a ReDoS vulnerability similar to CVE-2025-68142.
# A vulnerable regex is used within a simulated markdown block processor.
# The regex exhibits "catastrophic backtracking" when processing a crafted payload.
class VulnerableCaptionProcessor(BlockProcessor):
# This regex is a stand-in for the actual vulnerable pattern.
# The '(([a-z]|\s)+)+' part creates an exponential number of paths for the
# regex engine to check on certain inputs, causing the ReDoS.
VULNERABLE_REGEX = re.compile(r'^\^\^\s+((([a-z]|\s)+)+)')
def test(self, parent, block):
# The vulnerability is triggered here during the test phase.
# A malicious block will cause the regex search to hang.
return self.VULNERABLE_REGEX.search(block)
def run(self, parent, blocks):
# This part of the processor would normally create the HTML.
# For this PoC, we just need to consume the block.
blocks.pop(0)
return True
class VulnerablePymdownExtension(Extension):
def extendMarkdown(self, md):
# Register the vulnerable block processor.
md.parser.blockprocessors.register(
VulnerableCaptionProcessor(md.parser), 'vulnerable_caption', 105
)
# This payload is designed to exploit the ReDoS in the VULNERABLE_REGEX.
# It consists of a repeating character 'a' that fits '([a-z]|\s)+',
# followed by a '!' which causes the final match to fail.
# The failure forces the regex engine to backtrack through all N! possible
# combinations, leading to a long hang.
payload = "a" * 28 + "!"
malicious_markdown_content = f"^^^ {payload}"
print(f"Processing malicious markdown with payload of length {len(payload)}...")
print("The program will now hang for several seconds...")
start_time = time.time()
# This is the function call that will trigger the vulnerability and hang.
markdown.markdown(
malicious_markdown_content,
extensions=[VulnerablePymdownExtension()]
)
end_time = time.time()
duration = end_time - start_time
print(f"Processing finally completed after {duration:.2f} seconds.")
if duration > 2.0:
print("\nVulnerability successfully demonstrated: The hang was significant.")
else:
print("\nDemonstration failed: Processing was too fast.")Patched code sample
import re
def a_function_representing_the_vulnerable_code(payload: str):
"""
This function uses the original, vulnerable regular expression.
Executing this function with a malicious payload will cause a long hang,
demonstrating the ReDOS vulnerability.
WARNING: DO NOT RUN THIS IN A PRODUCTION ENVIRONMENT.
"""
# Vulnerable Regex from versions prior to 10.6.1. The alternation
# `(?:[^\t\n\r\f\v]| {1,3}(?=[^\t\n\r\f\v]))+` causes catastrophic backtracking.
vulnerable_re = re.compile(r'((?:[^\t\n\r\f\v]| {1,3}(?=[^\t\n\r\f\v]))+)(?:\n|(?<!\n)\Z)')
print("Executing vulnerable regex... this will hang.")
vulnerable_re.match(payload)
def a_function_representing_the_fix(payload: str):
"""
This function demonstrates the fix for the vulnerability.
It uses the patched regular expression from PyMdown Extensions 10.6.1,
which processes the same payload instantly.
"""
# The core of the fix is this patched regular expression, which avoids
# the ambiguous pattern that led to catastrophic backtracking.
fixed_re = re.compile(r'((?:[^\n\r\f\v]|\t(?! *\t))+)(?:\n|(?<!\n)\Z)')
return fixed_re.match(payload)
if __name__ == '__main__':
# This payload is crafted to trigger the ReDOS vulnerability in the original code.
malicious_payload = " " * 25 + "a"
print("--- Demonstrating the fix for CVE-2025-68142 ---")
print(f"Using payload: '{malicious_payload}'")
# Demonstrate that the fixed code executes quickly.
print("\n[1] Running the FIXED code:")
import time
start_time = time.perf_counter()
result = a_function_representing_the_fix(malicious_payload)
end_time = time.perf_counter()
print(f"Execution finished in {end_time - start_time:.6f} seconds.")
if result:
print("Result: Match found successfully (Vulnerability patched).")
else:
print("Result: No match found.")
# The following line is commented out by default because it will hang
# the script for a very long time, demonstrating the vulnerability.
# To test the vulnerability, you can uncomment the line below.
# print("\n[2] Running the VULNERABLE code (will cause a long hang):")
# a_function_representing_the_vulnerable_code(malicious_payload)Payload
^^^ !
Cite this entry
@misc{vaitp:cve202568142,
title = {{ReDoS in PyMdown Extensions' figure caption allows for Denial of Service.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-68142},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-68142/}}
}
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 ::
