← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-28784 · CWE-1336 · Disclosed 2026-03-04

Craft is a content management system

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a loaded nail gun locked in the maintenance closet, not a rifle lying in the lobby

CVE-2026-28784 is a Twig server-side template injection path in Craft CMS that can end in remote code execution on the web server. The vulnerable window spans Craft >= 4.0.0-RC1 and < 4.17.0-beta.1, plus >= 5.0.0-RC1 and < 5.9.0-beta.1; the vendor says production-safe stable fixes landed in 4.16.18 and 5.8.22. The exploit path uses the Twig map filter in text fields that accept Twig input under Settings in the control panel, or through the System Messages utility.

The vendor/NVD 7.2 HIGH score is technically defensible in a lab because successful exploitation is full server compromise. In the field, though, this is heavily gated: it needs authenticated control-panel access plus either administrator rights with allowAdminChanges enabled or a non-admin account with access to the System Messages utility. That is post-initial-access, narrow-population, and often blocked by sane Craft hardening, so the real-world patch priority comes down from HIGH to a solid MEDIUM.

"This is an admin-to-RCE trapdoor, not an internet-scale fire drill."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Gain privileged Craft control-panel access

The attacker first needs a valid Craft CMS account that can reach the control panel. The practical weapon here is not a public exploit kit but a stolen session, reused credentials, SSO bypass, or an already-compromised admin workstation. This prerequisite alone means the chain usually starts *after* initial access.
Conditions required:
  • Craft control panel is reachable to the attacker
  • Valid authenticated account exists
  • Account has admin rights or access to the System Messages utility
Where this breaks in practice:
  • Many Craft admin panels are VPN-restricted, IP-limited, or SSO-protected
  • MFA and identity telemetry should catch the account takeover step
  • System Messages utility access is uncommon outside select operators
Detection/coverage: External scanners will not validate this prerequisite. Identity logs, SSO events, MFA failures, and unusual control-panel logins are your best coverage.
STEP 02

Reach a Twig-accepting admin surface

The attacker then navigates to a field under Settings that accepts Twig input, or to the System Messages utility, and supplies a malicious payload. The abuse primitive is the Twig map filter path referenced in the advisory and associated patch work in craftcms/cms#18208. This is an application-feature abuse step, not generic web fuzzing.
Conditions required:
  • A vulnerable Craft version is installed
  • Affected UI path is present and reachable in the current role
  • allowAdminChanges is enabled for the admin path, or System Messages utility access exists
Where this breaks in practice:
  • Craft explicitly recommends allowAdminChanges=false in production
  • Some enterprises hide or operationally lock Settings changes in production
  • Least-privilege RBAC shrinks the set of users who can reach these screens
Detection/coverage: DAST and unauthenticated perimeter scanners are unlikely to hit this reliably. Application audit logs for Settings changes and System Messages edits are more relevant.
STEP 03

Trigger Twig SSTI into code execution

If the payload lands in a vulnerable context, Twig evaluates attacker-controlled content and the chain can reach remote code execution as the web application user. The patch series describes a move toward blocking dangerous non-Closure arrow-function behavior via Twig sandboxing controls. Successful execution hands the attacker arbitrary code on the Craft host, which is game over for that server.
Conditions required:
  • Payload reaches the vulnerable template-evaluation path
  • Application executes Twig in the affected mode
  • Host-level controls do not block the resulting child process or file abuse
Where this breaks in practice:
  • Modern EDR can catch suspicious child processes spawned by php-fpm, apache2, or nginx workers
  • Containerized or constrained PHP runtimes can reduce post-exec options
  • No public mass-exploitation evidence or broadly shared PoC was located
Detection/coverage: EDR and process-execution telemetry are the best tripwire here. Web logs alone may show only authenticated admin activity, not the final code-exec semantics.
STEP 04

Pivot from CMS host to broader environment

After code execution, the attacker can read application secrets, database credentials, SMTP/API keys, and potentially cloud metadata or adjacent services. On many Craft deployments, that means content database theft first and lateral movement second. The blast radius can still be serious, but it is bounded by a much narrower reachable population than a pre-auth internet RCE.
Conditions required:
  • Secrets are accessible from the Craft runtime
  • Network egress or local privilege paths permit follow-on activity
Where this breaks in practice:
  • Secret rotation, network segmentation, and IMDS protections can contain follow-on damage
  • Read-only containers or non-root service accounts limit persistence options
Detection/coverage: EDR, database access monitoring, outbound DNS/HTTP anomaly detection, and secrets-use alerts can expose the post-exploitation phase.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public evidence of active exploitation found in the reviewed sources. Not KEV-listed based on the current CISA KEV catalog.
Proof-of-concept availabilityI did not find a public exploit repo or turnkey PoC tied to CVE-2026-28784 or GHSA-qc86-q28f-ggww. Public technical detail exists in the GitHub advisory and patch PR, which is enough for a capable operator but not the same thing as mass-ready exploit code.
EPSS0.021% (6th percentile) per the GitHub Advisory Database entry. That is very low and consistent with a high-friction authenticated/admin-only path.
KEV statusNo as of 2026-06-02. No CVE-specific CISA KEV entry was found; use the catalog root as the authoritative check.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H = network reachable, easy to trigger *once you already hold high privileges*, no user interaction, and full CIA loss on the affected host. The PR:H term is the whole story here.
Affected versionsAdvisory package range: >= 4.0.0-RC1, < 4.17.0-beta.1 and >= 5.0.0-RC1, < 5.9.0-beta.1 per GHSA and GitLab GLAD.
Fixed versionsFirst unaffected package tags are 4.17.0-beta.1 and 5.9.0-beta.1, while the vendor says stable mitigation updates were shipped in 4.16.18 and 5.8.22 per NVD and the Craft GHSA. No distro backport guidance was found.
Exposure realityCraft itself is internet-exposed at non-trivial scale: a prior Craft-specific Censys advisory observed 144,333 exposed applications. That number is not specific to this CVE, but it shows the platform has meaningful public footprint. The exploitable subset for this CVE is much smaller because the vulnerable path sits behind authenticated admin/utility workflows.
Disclosure datePublished 2026-03-04 in NVD; GitHub security advisory published 2026-03-02 and GitHub Advisory Database published 2026-03-03.
Researcher / reporterCredited finder: RajChowdhury240. Reporter: rlarabee. Source: vendor GHSA.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.1/10)

The decisive factor is attacker position: this bug generally requires authenticated control-panel access plus elevated rights or a niche utility permission, which makes it a post-compromise amplifier, not an initial-access bug. The impact is severe on the individual host, but the reachable population is sharply reduced by PR:H, Craft's recommended allowAdminChanges=false production setting, and the rarity of System Messages access outside trusted operators.

HIGH Privilege/friction assessment
MEDIUM Public exploitation and PoC absence assessment

Why this verdict

  • Downgrade for attacker position: the chain starts with authenticated control-panel access, and the common branch needs high privileges. That implies prior compromise or insider access, which is a major downward adjustment from the vendor's 7.2 baseline.
  • Downgrade for deployment friction: the admin path also expects allowAdminChanges=true, while Craft explicitly recommends false in production. In mature deployments, that setting alone kills the easiest exploit path.
  • Keep it at MEDIUM, not LOW: if the attacker *does* have the needed role, the outcome is still server-side code execution with full confidentiality, integrity, and availability impact on the Craft host.

Why not higher?

This is not unauthenticated remote exploitation, and it is not broadly reachable across the exposed Craft population. The exploit chain requires either admin-level control-panel access plus a non-recommended production setting, or a narrower non-admin role with access to System Messages. There is also no KEV listing, no public exploitation evidence found, and EPSS is extremely low.

Why not lower?

Remote code execution is still remote code execution once the attacker clears the gate. If you already have exposed Craft admin surfaces, shared admin credentials, or weak SSO/MFA hygiene, this becomes a practical second-stage kill shot against the application server and its secrets. That keeps it above LOW.

05 · Compensating Control

What to do — in priority order.

  1. Set allowAdminChanges to false in production — Do this first because it directly cuts off the main admin-side exploit branch documented by Craft. For a MEDIUM verdict there is no noisgate mitigation SLA; go straight to the remediation window, but this config hardening should still be applied during the same change cycle because it meaningfully reduces exposure.
  2. Restrict control-panel exposure — Put the Craft control panel behind VPN, identity-aware proxy, IP allowlists, or equivalent admin-plane segmentation. This shrinks the pool of attackers who can even attempt the authenticated prerequisite; for a MEDIUM verdict there is no mitigation SLA — go straight to the 365-day remediation window, but this is worthwhile hygiene on any internet-facing CMS.
  3. Review and trim System Messages utility access — Audit which roles can reach the System Messages utility and remove it from everyone except the handful of operators who actually need it. This matters because the advisory explicitly names that utility as the alternative exploitation path when allowAdminChanges is disabled.
  4. Watch for PHP child-process anomalies — Tune EDR or host monitoring to alert on suspicious child processes from php-fpm, apache2, or nginx worker contexts, plus unusual shell, curl, wget, python, or package-manager execution. It will not prevent the bug, but it gives you a fighting chance to catch successful SSTI-to-RCE behavior.
  5. Enforce strong admin authentication — Backstop the biggest real-world prerequisite by enforcing MFA, SSO, impossible-travel alerts, and session revocation for Craft admins. The vulnerability's practical exploitability collapses if attackers cannot cheaply get or reuse privileged panel access.
What doesn't work
  • A perimeter WAF by itself won't save you; the vulnerable actions occur in authenticated admin workflows and the payload rides valid application semantics.
  • Relying on 'the panel isn't linked publicly' is not a control; if the URL is reachable and credentials are phished or reused, the prerequisite is satisfied.
  • Generic vulnerability scans often miss this because the path is authenticated, role-gated, and feature-specific.
06 · Verification

Crowdsourced verification payload.

Run this on the Craft CMS host or inside the application container, pointing it at the application root that contains composer.lock or vendor/. Example: sudo ./check-cve-2026-28784.sh /var/www/craft or ./check-cve-2026-28784.sh /srv/app. It needs read access to the app files and a working php binary for version parsing; root is not required if the app directory is readable.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check-cve-2026-28784.sh
# Detect likely exposure to CVE-2026-28784 in Craft CMS.
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error

set -u

TARGET="${1:-.}"

if ! command -v php >/dev/null 2>&1; then
  echo "UNKNOWN - php binary not found; cannot parse Craft version reliably"
  exit 2
fi

if [ ! -d "$TARGET" ]; then
  echo "UNKNOWN - target path does not exist: $TARGET"
  exit 3
fi

LOCKFILE="$TARGET/composer.lock"
INSTALLED_JSON="$TARGET/vendor/composer/installed.json"
CRAFT_COMPOSER_JSON="$TARGET/vendor/craftcms/cms/composer.json"

VERSION=""
SOURCE=""

if [ -f "$LOCKFILE" ]; then
  VERSION=$(php -r '
    $f=$argv[1];
    $j=json_decode(file_get_contents($f), true);
    if (!is_array($j)) exit(1);
    $pkgs=[];
    if (isset($j["packages"]) && is_array($j["packages"])) $pkgs=array_merge($pkgs,$j["packages"]);
    if (isset($j["packages-dev"]) && is_array($j["packages-dev"])) $pkgs=array_merge($pkgs,$j["packages-dev"]);
    foreach ($pkgs as $p) {
      if (($p["name"] ?? "") === "craftcms/cms") {
        echo $p["version"] ?? "";
        exit(0);
      }
    }
    exit(1);
  ' "$LOCKFILE" 2>/dev/null)
  if [ -n "$VERSION" ]; then SOURCE="$LOCKFILE"; fi
fi

if [ -z "$VERSION" ] && [ -f "$INSTALLED_JSON" ]; then
  VERSION=$(php -r '
    $f=$argv[1];
    $j=json_decode(file_get_contents($f), true);
    if (!is_array($j)) exit(1);
    $pkgs=[];
    if (isset($j["packages"]) && is_array($j["packages"])) {
      $pkgs=$j["packages"];
    } elseif (array_is_list($j)) {
      $pkgs=$j;
    } elseif (isset($j["versions"]) && is_array($j["versions"])) {
      $pkgs=array_values($j["versions"]);
    }
    foreach ($pkgs as $p) {
      if (($p["name"] ?? "") === "craftcms/cms") {
        echo $p["version"] ?? ($p["pretty_version"] ?? "");
        exit(0);
      }
    }
    exit(1);
  ' "$INSTALLED_JSON" 2>/dev/null)
  if [ -n "$VERSION" ]; then SOURCE="$INSTALLED_JSON"; fi
fi

if [ -z "$VERSION" ] && [ -f "$CRAFT_COMPOSER_JSON" ]; then
  VERSION=$(php -r '
    $f=$argv[1];
    $j=json_decode(file_get_contents($f), true);
    if (!is_array($j)) exit(1);
    echo $j["version"] ?? "";
  ' "$CRAFT_COMPOSER_JSON" 2>/dev/null)
  if [ -n "$VERSION" ]; then SOURCE="$CRAFT_COMPOSER_JSON"; fi
fi

if [ -z "$VERSION" ]; then
  echo "UNKNOWN - could not determine installed Craft CMS version from composer metadata"
  exit 2
fi

RESULT=$(php -r '
  $v=trim($argv[1]);
  $norm=preg_replace("/^v/i", "", $v);

  function vulnerable($ver) {
    # Known stable fixed versions from the vendor/NVD narrative:
    # 4.x fixed in 4.16.18, 5.x fixed in 5.8.22.
    # Advisory package ranges also note first unaffected prereleases:
    # 4.17.0-beta.1 and 5.9.0-beta.1.
    if (preg_match("/^4\./", $ver)) {
      return version_compare($ver, "4.0.0-RC1", ">=") && version_compare($ver, "4.16.18", "<");
    }
    if (preg_match("/^5\./", $ver)) {
      return version_compare($ver, "5.0.0-RC1", ">=") && version_compare($ver, "5.8.22", "<");
    }
    return null;
  }

  $isVuln=vulnerable($norm);
  if ($isVuln === true) {
    echo "VULNERABLE";
    exit(1);
  }
  if ($isVuln === false) {
    echo "PATCHED";
    exit(0);
  }
  echo "UNKNOWN";
  exit(2);
' "$VERSION")
STATUS=$?

echo "$RESULT - detected craftcms/cms version $VERSION from $SOURCE"
exit $STATUS
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: query your fleet for Craft CMS, identify every internet-exposed or externally reachable control-panel instance, and confirm whether it is on a vulnerable branch and whether allowAdminChanges is still enabled in production. Because this is MEDIUM, there is no noisgate mitigation SLA — go straight to the 365-day remediation window; still, harden the admin plane and disable allowAdminChanges in production during the next normal change cycle, then complete the actual vendor update within the noisgate remediation SLA of 365 days. If any affected Craft admin surface is public and weakly protected, move that subset to the front of the queue rather than treating the entire CVE as emergency patching.

Sources

  1. NVD CVE-2026-28784
  2. Craft CMS GitHub security advisory GHSA-qc86-q28f-ggww
  3. GitHub Advisory Database entry
  4. Craft guidance: Securing Craft / allowAdminChanges
  5. Patch PR #18208
  6. GitLab Advisory Database entry
  7. CISA Known Exploited Vulnerabilities catalog
  8. Censys Craft CMS exposure reference
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.