Stored XSS via Attribute Filter Bypass in league/commonmark
An XSS vulnerability in league/commonmark allows attackers to execute arbitrary JavaScript by prepending a U+000C form feed character to malicious attribute names, bypassing security filters in the AttributesExtension.
What's new
- 1. added coverage for commonmark (>= 0.6.0, < 2.9.1) Sep 2, 00:00 via ghsa
The AttributesExtension for the league/commonmark library fails to correctly sanitize attributes when a U+000C form feed character (\x0C) is prepended to the attribute name. The library's AttributesHelper uses PHP's trim() function to clean input, but since \x0C is excluded from the default trim character list, the character is preserved. This results in the validator failing to identify restricted attributes (such as onclick or onerror) or unsafe javascript: URIs. Because the subsequent HTML renderer does not escape attribute names, browsers interpret the malformed tag as a valid HTML element containing the malicious handler or URI. This vulnerability affects league/commonmark versions 2.7.0 through 2.9.0 and persists even when developers enable recommended security configurations, such as disabling allow_unsafe_links.
Attack Chain
- Attacker crafts a Markdown payload containing a malicious attribute or URI, prepending a U+000C character (e.g.,
{\x0Conclick="alert(1)"}). - The
league/commonmarkparser encounters the attribute string during Markdown conversion. - The
AttributesHelpermatches the attribute string, including the leading\x0C, via regex. - The code calls PHP
trim()on the attribute name, which fails to strip the\x0Ccharacter. - The library's
filterAttributes()function compares the sanitized attribute name against an allow-list; the presence of the hidden\x0Ccauses string comparisons to fail, allowing the malicious attribute to pass. - The
HtmlElementclass serializes the attribute into the final HTML output without additional validation or escaping. - The target's browser parses the emitted HTML, treating the
\x0Cas whitespace and executing the attacker's JavaScript payload.
Impact
Successful exploitation results in stored Cross-Site Scripting (XSS) in any application rendering untrusted Markdown using the AttributesExtension. Because payloads like onerror can be attached to image tags, the script executes automatically upon page load without requiring user interaction. This leads to session hijacking, unauthorized actions on behalf of the user, and potential account takeover.
Recommendation
Prioritize patching and configuration changes to mitigate the risk of XSS exploitation.
- Upgrade
league/commonmarkto version 2.9.1 or later immediately. - As a short-term workaround, define an explicit
allowlist for attributes (e.g.,['id', 'class', 'align']) in theAttributesExtensionconfiguration; this forces the library to reject attributes that do not match the allow-list regardless of prefix characters. - Audit applications using
league/commonmarkto determine if theAttributesExtensionis enabled and if inputs are retrieved from untrusted sources.
Immediate actions
Upgrade league/commonmark to version 2.9.1 or later.
Mitigations
Configure an explicit allow-list for the AttributesExtension.
XSS filter bypass