Craft CMS RCE: The Art of Property Injection
Jan 7, 2026·6 min read
Executive Summary (TL;DR)
CVE-2025-32432 is a 10.0 CVSS score nightmare for Craft CMS admins. It allows unauthenticated attackers to turn the `actionGenerateTransform` endpoint into an object instantiation machine. By leveraging Yii's `__class` property precedence and PHP gadget chains (specifically Guzzle and Yii's internal classes), attackers can achieve RCE. It's actively exploited in the wild.
A critical pre-authentication Remote Code Execution (RCE) vulnerability in Craft CMS exploits the underlying Yii framework's configuration mechanism. By passing a malicious array to an endpoint expecting a string, attackers can instantiate arbitrary classes and execute gadget chains leading to full system compromise.
The Hook: When Framework Magic Goes Dark
Craft CMS is often touted as the "premium" alternative to WordPress—cleaner code, better architecture, and less spaghetti. But under the hood, Craft relies heavily on the Yii framework, a PHP beast known for its "magic" methods and dependency injection capabilities. Usually, this magic makes a developer's life easier. Sometimes, however, it hands the keys to the kingdom to anyone with curl and a dream.
Meet CVE-2025-32432. It is the kind of vulnerability that keeps sysadmins up at night: Pre-Authentication Remote Code Execution. No credentials required. No complex race conditions. Just a logic flaw in how the application handles input types.
The vulnerability resides in the AssetsController. Specifically, an endpoint designed to generate image transforms—thumbnails, crops, that sort of thing. It sounds harmless, right? How much damage can you do resizing a JPEG? As it turns out, if you can convince the application that your "image handle" is actually a configuration array for a PHP class, the answer is: absolute, total carnage.
The Flaw: A Type Confusion Tragedy
The root cause is a classic case of "I expected a string, but I didn't check if you actually sent one." In AssetsController::actionGenerateTransform, the code grabs the handle parameter from the request body. The developer assumed handle would be a string identifier for a specific image transform (like thumb_200x200).
When this handle is passed to ImageTransforms::normalizeTransform(), it eventually gets fed into the constructor of a new ImageTransform($handle). Here is where the Yii framework's magic turns lethal. ImageTransform extends yii\base\Component. In Yii, Component constructors call Yii::configure($this, $config). This method iterates over the provided array and assigns values to the object's properties.
But wait, there's more. Yii has a concept called "Behaviors." If your configuration array has a key starting with as (like as myBehavior), Yii treats it as a behavior attachment. It tries to instantiate the class defined in that sub-array. The code tries to validate this by checking if the class inherits from yii\base\Behavior. It's a nice try at security, but it failed. Why? Because Yii::createObject prioritizes a magic key called __class. If you provide a valid behavior in the class key (to pass the check) but a malicious gadget in the __class key (to actually execute), Yii instantiates the malicious one. Checkmate.
The Code: The Smoking Gun
Let's look at the diff. It is painfully simple, which is characteristic of the most devastating bugs. The fix wasn't a rewrite of the image engine; it was literally three lines of type checking.
Vulnerable Code (Before):
$handle = $this->request->getRequiredBodyParam('handle');
// $handle is passed blindly to the ImageTransform constructor
// If $handle is an array, Yii::configure executes against it
$transform = new ImageTransform($handle);The Patch (After):
$handle = $this->request->getRequiredBodyParam('handle');
// The Shield: Verify type before processing
if (!is_string($handle)) {
throw new BadRequestHttpException('Invalid transform handle.');
}
$assetModel = Craft::$app->getAssets()->getAssetById($assetId);See that is_string($handle) check? That's the only thing standing between your server and a cryptominer. Without it, the $handle array flows down into Yii::createObject, allowing the attacker to define the class to be instantiated. This is Object Injection 101, but enabled by the framework's intended features rather than a raw unserialize() call.
The Exploit: From LFI to RCE
So we can create arbitrary objects. Now what? We need a "gadget chain"—a sequence of code execution triggered by property assignment or destruction. The researchers found two primary paths using classes already present in Craft's vendor folder.
Path 1: The Quick Probe (Guzzle FnStream)
Using GuzzleHttp\Psr7\FnStream, attackers can execute a simple function. The FnStream class has a destructor that calls a function stored in _fn_close. By injecting this object, an attacker can trigger phpinfo() to confirm vulnerability. It looks like this:
{
"action": "assets/generate-transform",
"handle": {
"as exploit": {
"class": "craft\\behaviors\\FieldLayoutBehavior", // Passes the check
"__class": "GuzzleHttp\\Psr7\\FnStream", // Actually instantiated
"_fn_close": "phpinfo" // The payload
}
}
}Path 2: The Kill Shot (Yii PhpManager)
To get a full shell, attackers use yii\rbac\PhpManager. This class has an init() method that calls include on a file path specified by the itemFile property. This turns the vulnerability into a Local File Inclusion (LFI). But how do we get a malicious file onto the server to include?
Session Poisoning: The attacker sends a request to a redirected admin page with a PHP payload in the URL (e.g., ?p=admin&a=<?php system($_GET['cmd']); ?>). Craft CMS dutifully logs this URL into the PHP session file on disk. The attacker then triggers the PhpManager gadget, pointing itemFile to /var/lib/php/sessions/sess_<SESSION_ID>. The server includes the session file, parses the PHP tags inside it, and executes the shell. Game over.
The Impact: Why You Should Panic
This is a 10.0 CVSS for a reason. It is not theoretical; it is being used right now by groups like "Mimo" to deploy malware.
- Full System Compromise: The attacker executes code as the
www-data(or equivalent) user. From there, they can elevate privileges or pivot to other internal systems. - Data Exfiltration: They have access to your
.envfile. That means your database credentials, AWS keys, Redis passwords, and encryption keys are gone. - Persistence: Attackers are dropping elusive webshells (renamed to look like legitimate vendor files, e.g.,
autoload_classmap.php) to maintain access even after you patch. - Resource Hijacking: Your expensive cloud instances are now mining Monero for someone in a basement halfway across the world.
The Fix: Closing the Window
If you are running Craft CMS 3, 4, or 5, and you haven't patched since April 2025, assume you are compromised. Seriously. Go check your logs.
Immediate Remediation: Update to the patched versions immediately:
- Craft 3 -> 3.9.15
- Craft 4 -> 4.14.15
- Craft 5 -> 5.6.17
Defense in Depth:
If you cannot update right this second (why not?), you can block the attack at the WAF level. Configure your firewall to drop any POST request to /actions/assets/generate-transform that contains the string __class or as in the body. However, note that sophisticated attackers might encode payloads to bypass simple string matching.
Post-Compromise Check:
Don't just patch and pray. Check storage/logs/web.log for requests to generate-transform with JSON bodies. Look for unexpected PHP files in your vendor or web directories. If you find evidence of exploitation, you need to rotate every single credential in your .env file. The attackers have read it.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:LAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Craft CMS Pixel & Tonic | 3.0.0-RC1 - < 3.9.15 | 3.9.15 |
Craft CMS Pixel & Tonic | 4.0.0-RC1 - < 4.14.15 | 4.14.15 |
Craft CMS Pixel & Tonic | 5.0.0-RC1 - < 5.6.17 | 5.6.17 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 (Code Injection) |
| Attack Vector | Network (Pre-Auth) |
| CVSS | 10.0 (Critical) |
| EPSS Score | 0.77564 (High Probability) |
| Exploit Status | Active / Weaponized |
| Platform | PHP / Yii Framework |
MITRE ATT&CK Mapping
Improper Control of Generation of Code ('Code Injection')
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.