Apr 24, 2026·6 min read·4 visits
An input validation flaw in Kirby CMS allows attackers to bypass XML escaping by starting input with a CDATA tag. Attackers can terminate the CDATA block early and inject arbitrary XML tags, potentially altering data structures in RSS feeds, sitemaps, or API responses. Upgrading to versions 4.9.0 or 5.4.0 patches the vulnerability.
Kirby CMS versions prior to 4.9.0 and 5.0.0 through 5.3.x are vulnerable to XML Injection (CWE-91). An insecure heuristic within the Toolkit's XML handling methods permits an attacker to bypass entity encoding by prepending a CDATA identifier. This allows the injection of arbitrary XML elements into documents generated by the CMS or custom plugins.
Kirby CMS utilizes the Kirby\Toolkit\Xml class and the Kirby\Data\Data class to handle formatting and encoding of XML data. These components are frequently leveraged by site-specific templates and plugins to generate structured outputs such as RSS feeds, Sitemaps, SOAP responses, and REST API payloads. Untrusted input processed by these methods must be strictly encoded to prevent structural alteration of the resulting document.
This vulnerability, tracked as CVE-2026-32870 and GHSA-9wfj-c55w-j9qr, constitutes an XML Injection (CWE-91). The flaw originates from an insecure validation heuristic used to detect strings that are already safely encapsulated. By exploiting this flaw, an attacker can construct a payload that bypasses the escaping routines entirely.
The attack surface includes the Xml::value(), Xml::tag(), Xml::create(), and Data::encode($input, 'xml') methods. If an application routes attacker-controlled input through any of these functions, the attacker can manipulate the syntax of the generated XML document. The core rendering engine for Kirby pages is largely unaffected by default, restricting the primary impact to custom features or plugins explicitly using the toolkit for XML generation.
The root cause resides in the Xml::value() method within src/Toolkit/Xml.php. This method prepares strings for XML output by applying htmlspecialchars() to prevent characters like < and > from being parsed as structural tags. The method attempts to optimize processing by skipping this escaping process if the input is already encapsulated in a Character Data (CDATA) section.
Prior to the patch, the application used a superficial validation check to identify pre-escaped data. It relied exclusively on Str::startsWith($value, '<![CDATA['). If an untrusted input string began with this specific sequence, the method assumed the entire string was a single, valid CDATA block and returned it verbatim without applying any entity encoding.
Because the validation logic only inspected the prefix of the string, an attacker could supply a payload that opens a CDATA block, immediately terminates it using ]]>, and then appends raw XML syntax. The vulnerable code would identify the prefix, skip the htmlspecialchars() function, and inject the mixed payload directly into the XML document. Downstream parsers would correctly terminate the CDATA section and then interpret the attacker's appended tags as legitimate structural elements.
The vulnerable implementation in src/Toolkit/Xml.php executed a simple string prefix match. The code lacked any verification regarding the termination of the CDATA block or the presence of malformed structural elements within the string payload.
// Vulnerable Implementation (Kirby < 4.9.0 / < 5.4.0)
public static function value(string $value): string
{
// Flaw: Only validates the start of the string.
// Attacker can terminate CDATA early and inject tags.
if (Str::startsWith($value, '<![CDATA[') === true) {
return $value;
}
return htmlspecialchars($value, ENT_QUOTES | ENT_XML1, 'UTF-8');
}The remediation, applied in commits a88ef33e81ed286d9dddf5a2f2d239aa73cd0d31 and 9309d5242fbfddf6650dc6c5fd8212765ce49ee6, enforces a strict structural contract. To bypass escaping, the string must now satisfy three conditions: it must start with a CDATA opener, end with a CDATA closer, and contain no internal unescaped CDATA closures.
// Patched Implementation (Kirby 4.9.0 / 5.4.0)
public static function value(string $value): string
{
// Fix: Validates complete encapsulation and prevents internal breaks
if (
Str::startsWith($value, '<![CDATA[') === true &&
Str::endsWith($value, ']]>') === true &&
Str::matches($value, '/\]\]>(?!<!\[CDATA\[|$)/') === false
) {
return $value;
}
// Any malformed or injected data falls through to htmlspecialchars
return htmlspecialchars($value, ENT_QUOTES | ENT_XML1, 'UTF-8');
}Exploitation requires an attacker to inject controlled input into a field processed by the Xml toolkit or the Data::encode() wrapper. Common injection points include blog post titles, user profile fields, or custom metadata that a site administrator later exports to an RSS feed or third-party integration.
The core exploitation technique leverages early termination of the CDATA block. An attacker supplies a payload formatted as <![CDATA[test]]><malicious_tag>payload</malicious_tag>. When the vulnerable application calls Xml::value() on this string, the prefix matches, and the raw payload is written to the output file.
When an external system parses the resulting document, the XML parser interprets <![CDATA[test]]> as a valid character block containing the word 'test'. The parser then encounters <malicious_tag> and evaluates it as an active document node. Attackers can also utilize comment smuggling techniques, such as <![CDATA[test]]><!-- comment -->, to hide injected logic or bypass WAF signatures looking for standard tags.
The proof-of-concept payloads from the patch test suite confirm that inputs like <![CDATA[ ]]> <juhu>haha</juhu> successfully bypass the escaping mechanism. In a patched environment, this same payload is safely encoded as <![CDATA[<![CDATA[ ]]]]><![CDATA[><juhu>haha</juhu>]]>, neutralizing the injection.
This vulnerability carries a CVSS 4.0 base score of 6.9, described by the vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:H/SA:N. The score reflects a high impact on system integrity, specifically the integrity of the generated XML documents, with no required privileges or user interaction.
The concrete impact depends heavily on the downstream consumer of the generated XML. If the XML is an RSS feed, an attacker can modify the feed's structure to inject unauthorized links or content. If the generated XML is a configuration file or a SOAP request used in a backend integration, the attacker may bypass business logic or trigger unexpected application behavior in the consumer system.
While the vulnerability permits arbitrary tag injection, it does not directly enable Remote Code Execution (RCE) on the Kirby server itself. Furthermore, if the generated XML is rendered directly in a web browser without proper content-type headers, the injected tags may result in Cross-Site Scripting (XSS).
The definitive remediation for CVE-2026-32870 is upgrading Kirby CMS to a patched release. Administrators must update installations to either version 4.9.0 or version 5.4.0. These releases contain the necessary regular expression fixes to enforce proper CDATA encapsulation.
For environments where immediate patching is unfeasible, developers should audit site-specific templates and plugins. Any calls to Kirby\Toolkit\Xml methods or Kirby\Data\Data::encode($val, 'xml') that process untrusted data must be identified. Developers can implement an interim manual sanitization layer by pre-processing user input with htmlspecialchars() before passing it to the Kirby toolkit.
Security teams can monitor for exploitation attempts by applying Web Application Firewall (WAF) rules or Intrusion Detection System (IDS) signatures. Rules should inspect incoming HTTP request bodies and URL parameters for the sequence <![CDATA[ immediately followed by ]]> within the same field payload, as this strongly indicates an attempt to exploit the parsing heuristic.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:H/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Kirby CMS Kirby | < 4.9.0 | 4.9.0 |
Kirby CMS Kirby | >= 5.0.0, < 5.4.0 | 5.4.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-91 |
| Attack Vector | Network |
| CVSS Base Score | 6.9 |
| Impact | High System Integrity |
| Exploit Status | Proof of Concept |
| Affected Component | Kirby\Toolkit\Xml |
Improper Neutralization of Special Elements used in an XML Document