This is a loaded nail gun locked inside the maintenance closet
CVE-2026-28783 is a Twig blocklist-bypass in Craft CMS that can turn privileged template access into PHP function execution. The affected lines are Craft 4 from 4.0.0-RC1 up to before 4.17.0, and Craft 5 from 5.0.0-RC1 up to before 5.9.0; the advisory text also references 4.17.0-beta.1 and 5.9.0-beta.1 during disclosure. The bug sits in Craft’s handling of non-Closure Twig arrow functions, where the denylist missed dangerous PHP-call paths.
The vendor/NVD-style 9.1 CRITICAL score overstates enterprise urgency because the decisive prerequisite is not network reachability, it is *already having high-value application privileges* or a compromised admin. In real fleets, PR:H here means post-initial-access, post-credential-theft, or a bad production practice like allowAdminChanges=true; that narrows the reachable population hard enough to downgrade this from internet-burner to authenticated-abuse.
4 steps from start to impact.
Find a reachable Craft control plane
X-Powered-By: Craft CMS or expose recognizable admin paths.- Target runs Craft CMS 4.x or 5.x in an affected range
- Attacker can reach the login or admin utility surface over the network
- Many Craft sites are public-facing, but the vulnerable action path is usually behind authenticated control-panel functionality
- Some operators suppress product headers, move the control panel to a separate domain, or gate admin access with VPN or IP allowlists
Gain privileged app access first
allowAdminChanges enabled on production, a compromised admin account, or an account with access to the System Messages utility. In practice that means phishing an admin, stealing a session, abusing SSO drift, or starting from an already-privileged insider account.- Compromised admin account, or
- A role with access to the System Messages utility, or
allowAdminChangesenabled in production
- This is the big one:
PR:Hmeans the attacker is already deep into the app trust boundary - MFA, SSO conditional access, and admin network segregation all break this step before the CVE matters
- Well-run Craft deployments explicitly set
allowAdminChanges=falsein production
Inject malicious Twig using a permitted feature
- Privileged user can modify a Twig-rendered field, template-like content, or system message path
- The target still permits the vulnerable non-Closure arrow-function behavior
- Not every privileged role can write to every Twig-rendered surface
- Newer projects may already have
enableTwigSandbox=true, which blocks the dangerous non-Closure arrow-function route - Change logging and approval workflows can expose suspicious template edits
Bypass the blocklist and execute server-side actions
- The crafted Twig reaches a vulnerable interpreter path
- The PHP runtime account has access to useful files, network targets, or execution primitives
- Containerized deployments, read-only filesystems, restricted egress, and non-privileged PHP-FPM users reduce blast radius
- The app server may still be isolated from crown-jewel systems even after code execution
The supporting signals.
| In-the-wild status | No public active-exploitation confirmation found in the sources reviewed, and CISA KEV does not list CVE-2026-28783 as of 2026-06-02. |
|---|---|
| Proof-of-concept availability | A public exploit write-up/PoC is indexed by CXSecurity and describes authenticated abuse of Twig template access; the vendor PR #18208 also exposes the defensive mechanics. |
| EPSS | 0.00036 (~0.036% 30-day exploitation probability, user-supplied intel). That is very low, and it lines up with the heavy privilege requirement. |
| KEV status | Not KEV-listed. Exact-CVE searches on CISA returned no result for CVE-2026-28783; only older Craft CMS issues appear in KEV. |
| CVSS vector reality check | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H says network and low complexity, but the operational truth is the PR:H field: attacker position is already privileged inside the application. |
| Affected versions | GitHub/OSV list Craft CMS >= 4.0.0-RC1, < 4.17.0 and >= 5.0.0-RC1, < 5.9.0 as affected; disclosure pages also referenced 4.17.0-beta.1 and 5.9.0-beta.1 during rollout. |
| Fixed versions | Vendor guidance points to Craft 4.17.0 and 5.9.0 as the resolved releases, with enableTwigSandbox called out as important for existing projects. I found no distro backport advisories in the reviewed sources. |
| Exposure population | Craft is a public-facing CMS, so the product family has real internet exposure. For context, a prior Censys Craft advisory observed 144,333 exposed Craft applications; that is population context, not a count of systems vulnerable to this CVE. |
| Disclosure and reporter | NVD/OSV show publication on 2026-03-04; the GitHub advisory was published 2026-03-02 and credits mHe4am as reporter. |
| Hardening nuance | Craft’s own Securing Craft guidance says new projects have enableTwigSandbox enabled by default, while existing projects need to turn it on explicitly. |
noisgate verdict.
The single biggest severity brake is the attacker position: this bug requires *already having privileged Craft access* or an unsafe production configuration, so it is not a practical first-hop internet exploit. The impact is absolutely real once reached, but the reachable population is much smaller than the 9.1 label implies.
Why this verdict
- Downgrade for attacker position:
PR:His not cosmetic here; it means the attacker already owns a privileged app account, an admin session, or a misconfigured production environment. - Downgrade for exposure fraction: plenty of Craft sites are public-facing, but only a subset expose the exact authenticated utility path *and* delegate the needed permissions beyond trusted admins.
- Downgrade for modern controls: MFA, SSO conditional access, admin VPN/IP gating, and EDR on web servers should stop the chain before or immediately after the vulnerable step.
- Keep it above LOW because impact is severe: if the attacker clears the privilege hurdle, the blast radius can jump to server-side code execution, arbitrary file reads, SSRF, and secret theft.
Why not higher?
Because this is not pre-auth and not broadly wormable. A CVE that requires internal authenticated privilege in a CMS control plane is inherently narrower than the classic public-edge RCEs that actually melt patch queues.
Why not lower?
Because the post-condition is still dangerous: this can become full web-tier compromise, not just a mild information leak. Also, Craft is often internet-facing, so the set of organizations with reachable admin surfaces is non-trivial even if the exploit path itself is privileged.
What to do — in priority order.
- Set
allowAdminChangestofalsein production — This directly removes one of the vendor-stated prerequisites and shrinks the reachable attack surface. For a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window, but this is cheap enough to do in the next normal change window. - Enable
enableTwigSandboxon existing projects — Craft’s own guidance says existing projects must explicitly enable this to get the stricter Twig behavior. For a MEDIUM verdict there is no mitigation SLA, so deploy during regular configuration management and complete before the remediation deadline. - Strip System Messages utility access from non-admin roles — The advisory explicitly names access to the System Messages utility as a sufficient condition. Audit RBAC now, remove that permission from editor/power-user roles where it is not strictly needed, and treat completion as part of the 365-day remediation window if not already standard.
- Gate admin access behind VPN or IP allowlists — This adds meaningful friction to the prerequisite stage by converting a public authenticated surface into a managed admin enclave. There is no mitigation SLA for a MEDIUM verdict, but this is a strong standing control for all CMS admin planes.
- Alert on control-plane Twig and system-message changes — Most orgs have poor visibility into CMS administrative content changes; add logging and alerting for edits to system messages, admin utilities, and template-bearing settings. This will not prevent the bug, but it shortens time-to-detect during the long remediation window.
- A generic WAF does not reliably help because the dangerous payload is delivered through authenticated admin functionality and looks like application-authorized content, not commodity edge exploit traffic.
- Unauthenticated perimeter scanning will miss the real risk because the decisive condition is privileged app access, not a banner or open port.
- MFA alone is not enough if the attacker already has a stolen admin session cookie or is operating as a malicious insider with valid privileges.
Crowdsourced verification payload.
Run this on the Craft application host from the project root, or pass the root as an argument: ./check-cve-2026-28783.sh /var/www/craft-app. It needs local read access to composer.lock, vendor/, and config/; no root is required unless file permissions are locked down.
#!/usr/bin/env bash
# check-cve-2026-28783.sh
# Detect likely exposure to CVE-2026-28783 in Craft CMS.
# Outputs one of: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error
set -u
APP_ROOT="${1:-.}"
if ! command -v php >/dev/null 2>&1; then
echo "UNKNOWN - php not found; cannot evaluate Craft version"
exit 2
fi
if [ ! -d "$APP_ROOT" ]; then
echo "UNKNOWN - app root not found: $APP_ROOT"
exit 3
fi
get_version_from_composer_lock() {
local lock="$1/composer.lock"
[ -f "$lock" ] || return 1
php -r '
$f = $argv[1];
$j = json_decode(file_get_contents($f), true);
if (!$j) exit(1);
$pkgs = array_merge($j["packages"] ?? [], $j["packages-dev"] ?? []);
foreach ($pkgs as $p) {
if (($p["name"] ?? "") === "craftcms/cms") {
$v = $p["version"] ?? "";
$v = ltrim($v, "vV");
echo $v;
exit(0);
}
}
exit(1);
' "$lock"
}
get_version_from_vendor() {
local cj="$1/vendor/craftcms/cms/composer.json"
[ -f "$cj" ] || return 1
php -r '
$f = $argv[1];
$j = json_decode(file_get_contents($f), true);
if (!$j) exit(1);
$v = $j["version"] ?? "";
if (!$v) exit(1);
echo ltrim($v, "vV");
' "$cj"
}
get_version_from_cli() {
if [ -f "$1/craft" ]; then
(cd "$1" && php craft --version 2>/dev/null) | awk '{print $NF}' | sed 's/^v//'
return 0
fi
return 1
}
version_compare_php() {
# args: v1 op v2
php -r 'exit(version_compare($argv[1], $argv[3], $argv[2]) ? 0 : 1);' "$1" "$2" "$3"
}
is_affected() {
local v="$1"
if version_compare_php "$v" ">=" "4.0.0-RC1" && version_compare_php "$v" "<" "4.17.0"; then
return 0
fi
if version_compare_php "$v" ">=" "5.0.0-RC1" && version_compare_php "$v" "<" "5.9.0"; then
return 0
fi
return 1
}
is_in_fixed_major() {
local v="$1"
if version_compare_php "$v" ">=" "4.17.0" && version_compare_php "$v" "<" "5.0.0"; then
return 0
fi
if version_compare_php "$v" ">=" "5.9.0"; then
return 0
fi
return 1
}
sandbox_enabled() {
# Best-effort check only. Existing projects may need this explicitly enabled.
local root="$1"
local files=""
[ -f "$root/config/general.php" ] && files="$files $root/config/general.php"
if [ -d "$root/config" ]; then
# shellcheck disable=SC2046
files="$files $(find "$root/config" -maxdepth 2 -type f \( -name '*.php' -o -name '*.php.dist' \) 2>/dev/null)"
fi
[ -n "$files" ] || return 1
grep -Eqs "enableTwigSandbox[[:space:]]*=>[[:space:]]*true|['\"]enableTwigSandbox['\"][[:space:]]*=>[[:space:]]*true" $files
}
VERSION=""
VERSION="$(get_version_from_composer_lock "$APP_ROOT" 2>/dev/null)" || true
if [ -z "$VERSION" ]; then
VERSION="$(get_version_from_vendor "$APP_ROOT" 2>/dev/null)" || true
fi
if [ -z "$VERSION" ]; then
VERSION="$(get_version_from_cli "$APP_ROOT" 2>/dev/null)" || true
fi
if [ -z "$VERSION" ]; then
echo "UNKNOWN - unable to determine Craft CMS version from composer.lock, vendor metadata, or CLI"
exit 2
fi
if is_affected "$VERSION"; then
echo "VULNERABLE - Craft CMS version $VERSION is in the affected range for CVE-2026-28783"
exit 1
fi
if is_in_fixed_major "$VERSION"; then
if sandbox_enabled "$APP_ROOT"; then
echo "PATCHED - Craft CMS version $VERSION is fixed and enableTwigSandbox appears enabled"
exit 0
else
echo "UNKNOWN - Craft CMS version $VERSION is fixed, but enableTwigSandbox was not confirmed from local config"
exit 2
fi
fi
echo "PATCHED - Craft CMS version $VERSION is outside the affected major/version ranges for CVE-2026-28783"
exit 0
If you remember one thing.
9.1 stamp hijack your queue. Reclassify this as a privileged-abuse Craft issue, quickly identify any internet-facing Craft 4/5 estates, and use normal change control to harden allowAdminChanges, enableTwigSandbox, and System Messages permissions; for a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window. Your noisgate remediation SLA is ≤365 days for the actual upgrade to 4.17.0 or 5.9.0, but public-facing CMS nodes with broad admin access deserve the front of that annual window, not the back.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.