Back Sploitus Exploit for Improper Neutralization of Special Elements Used in a Template Engine in Craftcms Craft_Cms
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.
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
