Feb 26, 2026·5 min read·160 visits
A critical RCE in Yii 2 (< 2.0.52) caused by a regression in a previous security fix. Attackers bypass class validation by using the `__class` key instead of `class`, allowing arbitrary object instantiation and code execution. Actively exploited.
History has a funny way of repeating itself, especially in PHP frameworks. CVE-2024-58136 is not just a vulnerability; it is a masterclass in how 'patching' can sometimes just mean 'moving the target.' In July 2024, the Yii team patched a critical RCE (CVE-2024-4990) involving behavior attachments. They added a check. It looked solid. But they forgot one tiny detail about their own Dependency Injection container: it speaks two languages. By simply using `__class` instead of `class`, attackers could bypass the check entirely, leading to active exploitation in the wild, particularly devastating for Craft CMS installations.
In the world of secure coding, the most dangerous moment is often immediately after a patch is released. Why? Because developers relax, and hackers start diffing. In July 2024, Yii Framework released version 2.0.51 to fix a nasty Remote Code Execution bug (CVE-2024-4990). The issue was simple: you could attach 'behaviors' (mixins) to components via user input, and if you attached a malicious class, you got RCE.
The fix seemed logical: before attaching a behavior, check if the requested class is actually a subclass of yii\base\Behavior. It’s like checking ID at the door of a club. If you aren't on the list, you don't get in.
But here is where the plot twists. The Yii Dependency Injection (DI) container is a polyglot. It accepts definitions in multiple formats. The developers checked one format (class), but the DI container happily prioritizes another (__class). It’s like the bouncer checking your ID, but the bartender serving anyone who whispers a secret password. This oversight turned a 'security fix' into a blueprint for bypass.
To understand this vulnerability, you have to understand how Yii creates objects. The Yii::createObject($config) method is the heart of the framework. It takes a configuration array and spits out an object. Usually, you specify the type of object using the class key.
However, for historical reasons or advanced configuration merging, Yii also supports __class. When Yii::createObject receives an array, it looks for __class first. If found, it uses that class name. If not, it falls back to class.
The vulnerability lies in yii\base\Component::__set. This magic method handles property assignment. When you try to assign a configuration to a property like as myBehavior, Yii attempts to attach it. In version 2.0.51, the code validated config['class']. It ensured config['class'] was a safe Behavior subclass.
But then it passed the entire config array to Yii::createObject.
So, if an attacker sends a payload with both keys:
class: "SafeBehavior" (The bouncer checks this. It's safe. Pass.)__class: "MaliciousGadget" (The factory uses this. It's deadly. Boom.)The validation logic and the execution logic were looking at different parts of the same data structure. This is a classic "Time-of-Check to Time-of-Use" (TOCTOU) style logic error, but purely with array keys.
Let's look at the diff. It is painfully simple, which makes it all the more tragic.
Here is the vulnerable code in 2.0.51:
// yii/base/Component.php
} elseif (isset($value['class']) && is_subclass_of($value['class'], Behavior::class, true)) {
// The check passes if 'class' is valid.
// But createObject might use '__class' if it exists in $value!
$this->attachBehavior($name, Yii::createObject($value));
}And here is the fix in 2.0.52:
// yii/base/Component.php
} elseif (
(isset($value['class']) && is_subclass_of($value['class'], Behavior::class)) ||
// NOW we check __class too
(isset($value['__class']) && is_subclass_of($value['__class'], Behavior::class))
) {
$this->attachBehavior($name, Yii::createObject($value));
}The fix is literally just acknowledging the existence of the shadow key. If you are a developer, write this on a sticky note: "Validate the data you use, not the data you expect."
So how do we turn this into a shell? We need a target application that allows user input to flow into a component configuration. Craft CMS was the unlucky winner here, exposing this via image transform settings.
The attack requires a "Gadget"—a PHP class that does something dangerous when instantiated or destructed. A classic favorite in the PHP ecosystem is GuzzleHttp\Psr7\FnStream. This class has a destructor that calls a user-defined function.
Here is what a weaponized JSON payload looks like:
{
"as trigger": {
"class": "yii\\behaviors\\AttributeBehavior",
"__class": "GuzzleHttp\\Psr7\\FnStream",
"__construct()": [[]],
"_fn_close": "system",
"methods": {
"close": "id"
}
}
}The Breakdown:
as trigger: Tells Yii to attach a behavior named "trigger".class: Points to AttributeBehavior. This satisfies the security check in 2.0.51.__class: Points to FnStream. This is what Yii::createObject actually builds._fn_close: The property on FnStream that holds the function name to call on destruction (system).close: The argument passed to the function.When the request ends, the FnStream object is destroyed, system('id') is executed, and the attacker wins.
This isn't theoretical. This is in the CISA Known Exploited Vulnerabilities (KEV) catalog. Attackers are actively scanning the internet for unpatched Yii applications, specifically Craft CMS.
Once RCE is achieved, the game is over. In the wild, we've seen attackers using this to:
db.php or .env files to get database credentials.Because this runs as the web server user (usually www-data), the attacker has full read/write access to the webroot. In modern containerized environments, they might just steal environment variables and move on. In legacy VPS hosting, they own the box.
If you are running Yii 2, check your composer.json immediately. If your version is < 2.0.52, you are vulnerable.
Remediation Steps:
composer update yiisoft/yii2. Ensure you land on at least 2.0.52.vendor/yiisoft/yii2/base/Component.php file manually if you cannot update immediately. Look for the __class check in __set.If you cannot patch (why?), you can try to mitigate this at the WAF level by blocking requests containing __class in the body, but this is risky and prone to false positives or bypasses (e.g., JSON encoding variations). Patching is the only real fix.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Yii Framework YiiSoft | < 2.0.52 | 2.0.52 |
Craft CMS Pixel & Tonic | Various (depends on Yii version) | Latest Stable |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502 (Deserialization of Untrusted Data) |
| CVSS v3.1 | 9.8 (Critical) |
| Attack Vector | Network (Remote) |
| Impact | Remote Code Execution (RCE) |
| Exploit Status | Active / Weaponized |
| KEV Status | Listed (2025-05-02) |
The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.