Aug 7, 2026·7 min read·1 visit
An authenticated remote code execution vulnerability exists in Craft CMS versions 4.x and 5.x. The flaw allows an attacker to bypass global configuration sanitization by nesting Yii2 behavior injection keys inside a JSON-encoded search condition property, resulting in command execution when the object is instantiated.
Craft CMS contains an authenticated remote code execution vulnerability due to a sanitization bypass in its search condition configuration parser. An attacker with access to the control panel can inject unsafe Yii2 behavior configurations wrapped inside a JSON-encoded string. When decoded and merged by the application, these keys bypass the global config cleanse filter and are evaluated by the Yii2 component factory, leading to arbitrary code execution.
Craft CMS utilizes the Yii2 PHP framework as its underlying foundation. In Craft CMS, the control panel allows administrative or authorized users to manage content and build complex element search queries. These search queries rely on dynamically constructed condition rules, which are represented as configuration arrays.
The dynamic instantiation model of Yii2 relies on helper functions such as Yii::createObject() to instantiate components from configuration arrays. Yii2 configurations can dynamically register behaviors using as <behaviorName> and bind event listeners using on <eventName>. To prevent untrusted input from specifying malicious behaviors, Craft CMS implements a global sanitizer method called Component::cleanseConfig().
The vulnerability arises because the search filter configurations are passed as JSON-encoded strings nested inside a config property of the main request data. This encapsulation hides dangerous keys during the primary sanitization run, allowing them to bypass filters. When the backend subsequently parses and merges the configuration, it instantiates components using the malicious properties, resulting in remote code execution.
In Yii2, custom components are dynamically instantiated based on developer-supplied configuration arrays. When an array is passed to an object creation sink, Yii checks for special properties. Specifically, keys starting with as dynamically attach behaviors to the target class, while keys starting with on register event handlers. This architecture exposes a significant attack surface if an attacker can manipulate these configuration keys.
To secure this mechanism, Craft CMS sanitizes incoming data using Component::cleanseConfig(). This method recursively scans arrays and deletes any keys prefixed with as or on. This implementation ensures that raw client requests cannot register arbitrary behaviors or event handlers.
However, the condition-handling logic in src/services/Conditions.php handles configuration data in a nested manner. In the control panel, search condition builders send the dynamic configuration serialized as a JSON string under the key config['config']. The application's global input sanitizer operates on the outer array structure first. Because the nested configuration is stored as a raw JSON string, the sanitizer does not inspect its contents, and the malicious keys survive the initial check.
When Conditions::createCondition() processes the input, it decodes the JSON string and merges the resulting associative array into the parent $config array. Because this decoded and merged array is not passed through cleanseConfig() a second time, the unsafe keys are restored to active status within the configuration array. The merged array is later processed by the Yii component factory, which loads the malicious behaviors or event handlers.
The critical failure is located in src/services/Conditions.php. The vulnerable codebase directly decodes the nested JSON and merges it with the main configuration without executing any sanitization on the decoded output. Below is the vulnerable implementation of the createCondition and createConditionRule methods:
public function createCondition(array|string $config): ConditionInterface
{
// The base config will be JSON-encoded within a `config` key if this came from a condition builder
if (isset($config['config']) && Json::isJsonObject($config['config'])) {
$config = array_merge(
Json::decode(ArrayHelper::remove($config, 'config')),
$config
);
}
// ...
}In the patched version, the application imports the craft\helpers\Component as ComponentHelper class to leverage the cleanseConfig() routine. The patch ensures that the decoded array is sanitized immediately upon deserialization, neutralizing any injected properties before the merge.
Below is the patched codebase showing how the data flow is secured in both createCondition and createConditionRule:
use craft\helpers\Component as ComponentHelper;
public function createCondition(array|string $config): ConditionInterface
{
if (isset($config['config']) && Json::isJsonObject($config['config'])) {
$config = array_merge(
// The decoded array is explicitly sanitized before merging
ComponentHelper::cleanseConfig(Json::decode(ArrayHelper::remove($config, 'config'))),
$config
);
}
// ...
}
public function createConditionRule(array|string $config): ConditionRuleInterface
{
// ...
} else {
// The rule configuration is sanitized before extracting the class name
$newConfig = ComponentHelper::cleanseConfig($newConfig);
$newClass = ArrayHelper::remove($newConfig, 'class');
}
// ...
}The patch completely eliminates the bypass vector by performing deep sanitization on any dynamic configuration data immediately after it transitions from a passive JSON string to an active PHP array. This prevents the Yii2 framework from ever encountering unsafe as or on directives during the dynamic instantiation of conditions or rules.
To exploit this vulnerability, an attacker must first obtain an authenticated session in the Craft CMS control panel. The session must possess sufficient privileges to trigger or modify element-search criteria, such as viewing or customizing elements in the control panel. Additionally, the attacker must supply a valid Cross-Site Request Forgery (CSRF) token with their HTTP requests to satisfy the application's request validation checks.
During a condition modification request, the attacker intercepts the outgoing HTTP POST request. The attacker injects a malicious payload into the config field within the condition parameter array. This payload is a JSON-encoded object containing a nested configuration structure. Inside this structure, the attacker embeds a key prefixed with as or on to define a custom behavior or register an event handler mapped to a dangerous class capable of executing system commands.
Because the backend decodes this JSON-encoded string and merges it into the configuration array used to construct the search condition, the Yii framework dynamically instantiates the object and attaches the specified behavior. Once attached, the behavior triggers system execution commands using framework utilities. This execution occurs under the permissions of the PHP-FPM or Apache process running the Craft CMS application.
The execution of the injected behavior triggers a semi-blind RCE. Because the web application's endpoint returns a standard JSON HTTP response rather than piping system shell outputs directly back in the response, attackers must verify code execution using side effects. Attackers often verify code execution by writing a payload verification file to a web-accessible directory on the server and calling it via a secondary request.
Successful exploitation of this vulnerability grants the attacker arbitrary code execution on the underlying hosting server. Because execution occurs within the context of the web server daemon, the attacker can execute system commands, write files to writable directories, or read arbitrary local files. This leads to a complete compromise of the application and its hosting environment.
From a data confidentiality standpoint, an attacker can access sensitive configuration files, such as the .env file containing database credentials, encryption keys, and API credentials. By reading the database credentials, the attacker can compromise all application tables, exposing user data, cryptographic hashes, and business-critical records.
Additionally, the attacker can perform lateral movement or establish persistence on the server by installing web shells or modifying application scripts. Given that the vulnerability does not require administrative permissions but only an authenticated control panel user with permission to use search filters, the attack surface is broader than typical admin-only remote code execution bugs.
The primary remediation strategy is upgrading Craft CMS to a patched release. For environments running the 5.x branch, administrators must upgrade to version 5.10.6 or later. For environments running the 4.x branch, the installation must be upgraded to version 4.18.2 or later. Upgrades can be performed via Composer by running composer update craftcms/cms in the project root directory.
For instances where an immediate upgrade is not feasible, organizations should implement strict access controls on the control panel. Restricting access to trusted IP ranges or VPNs minimizes the risk of unauthorized sessions exploiting the vulnerability. Additionally, monitoring web server logs for anomalous POST requests containing nested as or on properties within search configurations can help detect exploitation attempts.
Security teams should also deploy Web Application Firewall (WAF) rules to detect and block payloads containing JSON-escaped sequences representing malicious keys. Specifically, rules should monitor incoming traffic for patterns such as \"as \" or \"on \" nested inside the config or condition parameter values of POST requests.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Craft CMS Craft CMS | >= 5.0.0-RC1, < 5.10.6 | 5.10.6 |
Craft CMS Craft CMS | >= 4.0.0-RC1, < 4.18.2 | 4.18.2 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Improper Control of Generation of Code ('Code Injection') (CWE-94) / Deserialization of Untrusted Data (CWE-502) |
| Attack Vector | Network (Authenticated session required with access to search filters) |
| CVSS Score | 8.7 (High) |
| Exploit Status | None / Proof-of-Concept |
| KEV Status | Not Listed |
Improper Control of Generation of Code ('Code Injection')
A Denial of Service vulnerability exists in the league/commonmark package for PHP when using the XML rendering subsystem. Due to unconstrained indentation based on AST depth, rendering deeply nested elements leads to asymmetric resource consumption (quadratic output size complexity).
An authenticated remote code execution vulnerability exists in Craft CMS due to a flaw in how the Twig template sandbox policy handles class-level allowlists. Prior to the fix, the security policy allowed arbitrary public methods from parent classes of allowed interfaces, allowing authenticated attackers to invoke Yii component methods such as attachBehavior on element models to load arbitrary classes and execute system commands.
A high-severity authorization bypass vulnerability in Craft CMS allows authenticated users to reset arbitrary user passwords, including administrator accounts, by exploiting a mass assignment vulnerability in the User element model.
jsoup is a widely used Java library for working with real-world HTML. Versions 1.14.3 up to but excluding 1.23.1 contain a Cross-Site Scripting (XSS) vulnerability. When an application configures a custom Safelist that explicitly permits certain raw-text or RCDATA elements, such as style, title, or iframe, an attacker can exploit a parser-browser desynchronization flaw to bypass sanitization. This is achieved by utilizing trailing ASCII control characters that are handled differently by the HTML5 parsing specification and Java's string normalization methods, resulting in unescaped markup execution on the client side.
The ngx-extended-pdf-viewer library embeds a version of Mozilla's pdf.js that contains vulnerability CVE-2026-16633. This vulnerability allows arbitrary JavaScript execution (XSS) upon rendering a malicious PDF file.
A denial-of-service vulnerability in node-re2 prior to version 1.25.1 allows attackers to trigger uncatchable native assertion failures in the Google V8 engine. By supplying output-amplifying replacement templates, an attacker can exceed V8 string limits, resulting in an immediate process crash.