CVE-2025-46656
markdownify < 0.14.1 allows large headline prefixes, causing memory consumption.
- CVSS 3.3
- CWE-1284
- Resource Management
- Remote
python-markdownify (aka markdownify) before 0.14.1 allows large headline prefixes such as <h9999999> in addition to <h1> through <h6>. This causes memory consumption.
- CWE
- CWE-1284
- CVSS base score
- 3.3
- Published
- 2025-04-26
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Algorithm
- Code defect classification
- Incorrect Functionality
- Category
- Resource Management
- Subcategory
- Resource Management
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- python-markd
- Fixed by upgrading
- Yes
Solution
Upgrade to version 0.14.1 or later.
Vulnerable code sample
import markdownify
import time
def demonstrate_cve_2025_46656(prefix_length):
"""
Demonstrates potential memory consumption issue with large headline prefixes
in markdownify before version 0.14.1. This is a simplified simulation
as the exact internal workings of the vulnerable code are not fully known.
Args:
prefix_length: The number of '9's to use in the <h> tag prefix.
"""
html_input = f"<h{ '9' * prefix_length }>This is a large headline</h{ '9' * prefix_length }>"
start_time = time.time()
try:
markdown_output = markdownify.markdownify(html_input) # Simulate pre-0.14.1 behavior
print(f"Markdown output: {markdown_output}")
except Exception as e:
print(f"Error during conversion: {e}")
end_time = time.time()
print(f"Conversion took {end_time - start_time:.4f} seconds")
if __name__ == "__main__":
# Test with increasing prefix lengths to observe potential slowdown
# and (if memory is limited) potential memory errors. Adjust the range
# depending on your system resources.
for prefix_length in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: # increase this range at your own risk
print(f"\nTesting with prefix length: {prefix_length}")
demonstrate_cve_2025_46656(prefix_length)Patched code sample
import re
def markdownify(html):
"""
Converts HTML to Markdown, mitigating CVE-2025-46656.
"""
def replace_headline(match):
level = int(match.group(1))
if 1 <= level <= 6: # Limit heading levels to 1-6
return "#" * level + " " + match.group(2) + "\n"
else:
return match.group(2) + "\n" # Treat as regular text if invalid level
# Use a regular expression to find all headlines. The r'\d+' pattern is crucial
# for extracting the heading level while avoiding excessive memory consumption from large levels.
html = re.sub(r'<h(\d+)>(.*?)</h\1>', replace_headline, html, flags=re.IGNORECASE)
# Basic text processing (replace with more comprehensive logic as needed)
html = html.replace('<p>', '').replace('</p>', '\n')
html = html.replace('<b>', '**').replace('</b>', '**')
html = html.replace('<i>', '*').replace('</i>', '*')
return html
if __name__ == '__main__':
# Example Usage: This is where the vulnerability would be triggered in older versions.
html_input = "<h9999999>This is a very large heading!</h9999999><p>Some text.</p>"
markdown_output = markdownify(html_input)
print(markdown_output)
html_input2 = "<h1>Normal heading</h1>"
markdown_output2 = markdownify(html_input2)
print(markdown_output2)
html_input3 = "<h7>Invalid heading</h7>"
markdown_output3 = markdownify(html_input3)
print(markdown_output3)Payload
<h9999999>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</h9999999>
Cite this entry
@misc{vaitp:cve202546656,
title = {{markdownify < 0.14.1 allows large headline prefixes, causing memory consumption.
}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-46656},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-46656/}}
}
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 ::
