Skip to content
Exploit for Improper Neutralization of Special Elements Used in a Template Engine in Craftcms Craft_Cms

Exploit for Improper Neutralization of Special Elements Used in a Template Engine in Craftcms Craft_Cms

Sploitus • September 27, 2026

CVE-2026-28695 patch bypass — Craft CMS ≤ 5.9.8 authenticated RCE (`element- `)

====================================================================================

CWE : CWE-502 (Unsafe Object Instantiation) + CWE-1336 (SSTI origin)

Base : CVE-2026-28695 (SSTI via create() + Symfony Process)

Fix : advisory fix e31e508 (restricts create() to BaseObject) — BYPASSED here

Affects : Craft CMS ≤ 5.9.8 with the CVE-2026-28695 fix applied

Authenticated, **blind** remote code execution in Craft CMS. The official fix for

CVE-2026-28695 only restricts `create()` to `yii\base\BaseObject` subclasses. The

`yii\behaviors\AttributeTypecastBehavior` gadget **is** a `BaseObject` yet still reaches a

`call_user_func()` sink, so it survives the filter. Delivered through the non-Twig

`admin/actions/element- / ` action, it also side-steps `CRAFT_ENABLE_TWIG_SANDBOX`.

* Why the CVE-2026-28695 PoC is dead on 5.9.8

Craft's `element- ` controller deserialises attacker JSON into live PHP objects via

Yii's `Craft::createObject()` / `Instance` machinery. By attaching an `AttributeTypecastBehavior`

whose `attributeTypes` maps an attribute to the callable `[Psy\Readline\Hoa\ConsoleProcessus, execute]`

and firing the `beforeSave` event, the typecast becomes:

call_user_func([Psy\Readline\Hoa\ConsoleProcessus, "execute"], "")

The endpoint always answers **HTTP 500** and returns no command output — execution is blind.

Why the CVE-2026-28695 PoC is dead on 5.9.8

-------------------------------------------

* **CVE-2026-28695** = SSTI via `create()` + Symfony `Process`, reached from a Twig sink

(typically `Entry Types → Title Format`).

* On 5.9.8 the fix commit **`e31e508`** restricts `create()` to `BaseObject` subclasses, and

the target also runs with `CRAFT_ALLOW_ADMIN_CHANGES=false` (settings → 403) **and**

`CRAFT_ENABLE_TWIG_SANDBOX=true`. The documented vector is triple-closed.

* Lesson: confront the **exact version** against the **fix commit**, don't fire on the CVE

name. Here the fix is *older* than the deployed build → look for a filter bypass, not the

yii\behaviors\AttributeTypecastBehavior -> Psy\Readline\Hoa\ConsoleProcessus::execute

1. **Sandbox** — `element- ` is a PHP object path, **not** a Twig render.

`CRAFT_ENABLE_TWIG_SANDBOX` only gates the template engine, so it never applies.

2. **Patch** — the fix restricts `create()` to `BaseObject`. `AttributeTypecastBehavior`

→ `yii\base\Behavior` → `yii\base\BaseObject`, so it **satisfies** the new constraint

while still offering an execution sink. The fix closes *one* family of classes, not the

POST /index.php?p=admin/actions/element- /

`Hoa\ConsoleProcessus::execute()` runs the command through **`escapeshellcmd()`**:

| POST users/login (jenny:******) |

|--------------------------------------->|

| GET /admin/dashboard (JS csrf) |

|--------------------------------------->| call_user_func(

| HTTP 500 (blind, no output) | [ConsoleProcessus,execute], CMD)

| |

| oracle: "sleep 5" -> +~5s latency |

| shell: socat TCP-LISTEN:4445 bind |

| Status | Versions |

|------------|------------------------------------------------------|

| Vulnerable | Craft CMS ≤ 5.9.8 **with** the CVE-2026-28695 fix |

| Note | The gadget requires an authenticated CP session |

| File | Role |

|--------------|-------------------------------------------------------------|

| `exploit.py` | PoC: login → timing-oracle check → single cmd → bind shell |

| `README.md` | This file |

Requires `requests` (`pip install requests`).

# 1. Prove code execution (timing oracle: sleep 5 must add ~5s)

python3 exploit.py --check

# 2. Fire a single blind command (one binary + args, no metachars)

python3 exploit.py "sleep 5"

# 3. Plant a persistent bind shell as the web user, then connect

python3 exploit.py --bind 4445

python3 exploit.py -u User -p 'SecureP4$$!' --bind

1. Scrape the login CSRF, authenticate to the CP, pull the dashboard action CSRF.

2. Send the `AttributeTypecastBehavior` gadget to `element- `.

3. `--check` proves blind exec by injected latency; `--bind` drops a `socat` listener.

1. Upgrade Craft CMS to a release that constrains object instantiation on **all** deserialised

input paths (not just `create()`), and audit `element- ` condition handling.

2. Keep `CRAFT_ALLOW_ADMIN_CHANGES=false` **and** minimise Content Editor privileges — auth is

3. Remove/upgrade the bundled `psy/psysh` (Hoa) dependency where a `ConsoleProcessus::execute`

4. WAF/telemetry: alert on `element- / ` bodies containing `__class`, `as rce`, or

`AttributeTypecastBehavior`; flag bursts of HTTP 500 from that action.

* Craft CMS fix commit `e31e508` (create() → BaseObject restriction)

* Yii `AttributeTypecastBehavior` / `yii\base\Security` docs

This project is for authorized security testing, education, and defensive research only.

You are responsible for complying with applicable laws and the rules of engagement of your lab or client.

Extracted Entities

Attack Types (1)

Platforms (2)

Tools (1)