Sep 2, 2026·6 min read·1 visit
Unauthenticated remote attackers can exhaust server CPU resources by submitting crafted Markdown payloads containing specific repetition patterns, causing application-wide denial of service.
The league/commonmark library is subject to multiple denial of service vulnerabilities. These stem from three independent algorithmic complexity weaknesses in Markdown parsing: regular expression backtracking, reference link normalization, and delimiter processing. Remote, unauthenticated attackers can exploit these flaws by submitting crafted Markdown input to exhaust CPU execution resources, leading to application-wide thread exhaustion.
The league/commonmark library is the standard Markdown parsing package for the PHP ecosystem. Under default configurations, it exposes three independent algorithmic complexity weaknesses. These flaws reside in different parts of the parser and do not require custom extensions to trigger. An unauthenticated remote attacker can exploit these weaknesses to cause application-wide Denial of Service.
Algorithmic complexity attacks exploit vulnerabilities where the execution time of an algorithm grows non-linearly with input size. In these cases, very small payloads can lead to severe CPU utilization. This is distinct from network-level flooding attacks because it achieves resource exhaustion using minimal attacker bandwidth.
The vulnerabilities affect three separate execution paths within the library. These are the fenced code block parser, the reference link label normalizer, and the delimiter stack processing engine. When processing maliciously formatted input, the PHP engine wastes significant CPU cycles performing redundant operations, stalling the thread and preventing the system from answering legitimate web requests.
The first path is a Regular Expression Denial of Service (ReDoS) vulnerability in FencedCodeStartParser.php. The parser used a greedy, non-possessive quantifier inside a regular expression to match the opening of code blocks. The regular expression pattern featured a negative lookahead assertion. When the engine failed to find a matching sequence, the non-possessive quantifier forced exhaustive backtracking over each character of the input line.
The second path is a quadratic string normalization flaw in CloseBracketParser.php. When processing reference link brackets, the parser extracts the label and queries the ReferenceMap. Before searching, the library normalizes the label by running multiple passes of high-overhead functions including trim, preg_replace, and multi-byte string functions. In documents containing nested brackets, this normalization is evaluated repeatedly on overlapping substrings, resulting in quadratic time complexity.
The third path lies in the delimiter processing engine where the parser tracks openers and closers for style delimiters. To avoid quadratic back-scanning, the engine relies on a memoization cache using search boundaries. However, because the cache keys included the unclamped raw length of the closing delimiters, an attacker could supply closers of varying lengths to generate distinct keys. This bypassed the cache entirely, falling back to sequential back-scans of the entire stack.
The combination of these three issues represents a severe exposure for applications accepting user-submitted Markdown. An attacker can target any or all of these paths depending on the exact parsing features allowed by the application.
The first fix makes the greedy quantifier possessive in the code block parser. Making the quantifier possessive prevents the engine from backtracking into the matched group of backticks upon subsequent match failures. The execution time is successfully reduced from quadratic to linear.
// Before the fix
$fence = $cursor->match('/^[ \\t]*(?:`{3,}(?!.*`)|~{3,})/');
// After the fix
$fence = $cursor->match('/^[ \\t]*(?:`{3,}+(?!.*`)|~{3,})/');The second fix introduces an early length-based escape check in the bracket processor. According to the CommonMark specification, link labels are capped at 999 characters. By measuring the length of the raw bracket sequence before executing any copy or normalization logic, the parser drops oversized spans early.
$start = $opener->getPosition();
$length = $startPos - $start;
// Reject spans longer than the maximum link label length
if ($length > 999) {
$cursor->restoreState($savePos);
return null;
}The third fix limits the cache-key variations generated by delimiters. By capping the delimiter length used for the key generation, the cache key space becomes finite and small. This ensures that even if an attacker passes extremely long sequences of varying delimiters, they will hit existing cache buckets and preserve the linear-time lookup guarantee.
// Clamping the closer length to 2 for emphasis
\\min($closer->getLength(), 2);
// Clamping the closer length to 3 for strikethrough/highlight
\\min($closer->getLength(), 3);Exploitation requires no special privileges and can be performed remotely over HTTP. The target endpoint must accept raw Markdown input from users and render it using a vulnerable version of league/commonmark. Common targets include comment sections, blog platforms, profile bios, and messaging features.
To exploit the ReDoS vulnerability in the fenced code block parser, the attacker sends a payload with a massive run of opening backticks followed by regular text and a single trailing backtick. When the regex engine evaluates the trailing backtick, it is forced to backtrack across the massive run, keeping the execution thread at 100% CPU.
``````````````````````````````````[200,000 backticks]`````````````````````````````````a`To exploit the reference link lookup vulnerability, the attacker registers a single legitimate reference link definition and then provides deeply nested bracket structures. The parser will evaluate every nested bracket level, invoking the heavy string normalization functions repeatedly.
[x]: y
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[a]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]The impact of successful exploitation is complete Denial of Service. Because PHP-FPM and similar execution models rely on a fixed number of worker threads, blocking several threads with CPU-heavy loops quickly exhausts the pool. When the worker pool is exhausted, the server can no longer process incoming requests, resulting in gateway timeouts (HTTP 504) for all visitors.
The vulnerability is classified as High severity with a CVSS score of 7.5. The vector breakdown is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. This indicates that the vulnerability can be exploited over the network without user interaction or authentication, causing a complete loss of availability.
While there is no threat to confidentiality or integrity, the operational impact of a persistent Denial of Service is significant. Attackers can continually submit malicious payloads to keep worker processes locked indefinitely, effectively taking down the target platform with minimal outbound bandwidth requirements.
The most effective resolution is upgrading league/commonmark to version 2.9.1 or later. This release addresses all three vulnerabilities while maintaining backward compatibility with existing integration patterns.
composer update league/commonmarkIf upgrading is not immediately possible, applications should deploy input validation filters. Implementing a pre-parsing check to reject raw Markdown submissions containing lines longer than 2,000 characters is a highly effective mitigation. This restricts the scale of backtracking and normalization loops before the library processes the input.
function preValidateMarkdown(string $content): bool {
$lines = explode("\\n", $content);
foreach ($lines as $line) {
if (strlen($line) > 2000) {
return false;
}
}
return true;
}Additionally, operations teams should configure strict execution timeouts in their PHP environment. Lowering max_execution_time in php.ini or configuring request_terminate_timeout in PHP-FPM configuration ensures that threads locked by malicious inputs are terminated automatically, freeing up workers to handle legitimate traffic.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
league/commonmark thephpleague | >= 0.6.0, < 2.9.1 | 2.9.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-407, CWE-1050, CWE-1333 |
| Attack Vector | Network (Unauthenticated) |
| CVSS v3.1 | 7.5 |
| Impact | High (Denial of Service) |
| Exploit Status | Proof-of-Concept Available |
| First Patched Version | 2.9.1 |
The library performs operations that can be forced into quadratic or super-linear time complexity via crafted inputs, allowing resource exhaustion.
GHSA-8423-8FGW-73VQ is a pre-authentication denial of service vulnerability in the Tornado web server's handling of multipart/form-data. The flaw allows an unauthenticated remote attacker to cause memory exhaustion and CPU starvation by transmitting a crafted HTTP request containing a high density of boundary delimiters. Because Tornado splits the entire request body in memory prior to enforcing the max_parts validation threshold, the Python interpreter attempts to materialize a massive list of byte segments. This triggers an immediate memory exhaustion (OOM) crash or server-wide CPU starvation before the payload can be validated and rejected.
An inconsistency in whitespace handling between the PCRE regex engine, PHP's native trim function, and web browsers allows unauthenticated attackers to bypass XSS protections in the league/commonmark AttributesExtension by injecting a Form Feed (U+000C) control character.
GHSA-JJV6-8J6V-6J52 details multiple algorithmic complexity issues in the SmartPunct and Attributes extensions of the league/commonmark PHP library, leading to high CPU consumption and Denial of Service (DoS) when parsing pathological Markdown inputs.
An Untrusted Search Path (CWE-426) vulnerability exists in the Natural Language Toolkit (NLTK) library when executing the Graphviz 'dot' utility. Because the library fails to enforce absolute paths when executing external commands, local attackers can plant a malicious binary named 'dot' inside the current working directory. The library then executes the malicious binary, resulting in local arbitrary code execution under the context of the running Python process.
An algorithmic complexity vulnerability (CWE-407) in the AttributesExtension of league/commonmark allows unauthenticated remote attackers to cause CPU exhaustion and Denial of Service (DoS) via crafted Markdown payloads containing adjacent or consecutive attributes.
A stored Cross-Site Scripting (XSS) vulnerability exists in sanitize-html from version 1.9.0 up to 2.17.6. The flaw permits attackers to bypass scheme-policy enforcement using SVG SMIL animation elements targeting URL attributes with semicolon-separated URI lists.