← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-28697 · 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 not a break-in at the front door, it is an insider with the master key finding an unlocked server room

CVE-2026-28697 is an authenticated admin-side SSTI-to-RCE flaw in Craft CMS. A user with an administrator account can inject Twig into system message or email template fields, call craft.app.fs.write() or related paths, drop a PHP webshell into a web-accessible filesystem or volume, and then execute OS commands through the browser. The affected ranges documented by GitHub/NVD are >= 4.0.0-RC1, < 4.17.0-beta.1 and >= 5.0.0-RC1, < 5.9.0-beta.1.

The vendor/NVD CRITICAL label reflects raw impact after exploitation, not the real attack chain. In practice this is post-auth, admin-only, frequently gated by allowAdminChanges, and further narrowed by needing a writable web-accessible filesystem or volume plus a reachable template-render path. That makes it a serious containment problem after control-plane compromise, but not an internet-wormable or broad pre-auth emergency.

"Dangerous on a single host, but it starts at admin-level auth and usually dies on production hardening."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Gain Craft admin control

The attacker needs a valid Craft administrator session or equivalent access to the System Messages utility. Weaponization here is usually boring: stolen credentials, session theft, SSO abuse, or an already-compromised admin workstation rather than a bespoke exploit chain. This prerequisite matters more than the RCE itself because it means the attacker is already inside the control plane.
Conditions required:
  • Authenticated administrator account, or delegated access to the System Messages utility
  • /admin reachable from the attacker position
  • MFA/SSO protections bypassed, absent, or already satisfied
Where this breaks in practice:
  • Modern enterprises often front admin panels with SSO, MFA, VPN, or IP allowlists
  • A compromised CMS admin is already a substantial security event; many orgs would catch it before follow-on exploitation
  • Craft production guidance explicitly recommends disabling allowAdminChanges
Detection/coverage: External scanners cannot prove this step without valid credentials. Expect coverage from IAM logs, IdP sign-in telemetry, reverse proxy logs, and EDR on the admin endpoint rather than unauthenticated vuln scanning.
STEP 02

Inject Twig payload into system messages

Using the built-in utility, the attacker edits a system email template and inserts a Twig SSTI payload. The published PoC uses craft.app.fs.getFilesystemByHandle(...).write(...) or volume-backed writes to place attacker-controlled PHP into storage exposed by the web server. The GHSA includes working payload examples from reporter mHe4am.
Conditions required:
  • Access to Utilities → System Messages or equivalent template-edit path
  • allowAdminChanges enabled, or utility permissions delegated
  • A valid filesystem or volume handle known to the attacker
Where this breaks in practice:
  • Many production sites disable allowAdminChanges=false, removing the easiest path
  • Not every deployment exposes a writable filesystem or volume back to the web root
  • Attackers need enough product knowledge to pick a handle that resolves to a reachable path
Detection/coverage: Config drift monitoring, admin audit logs, database change auditing, and file integrity monitoring can catch modified system messages or newly written .php files. Commodity network scanners generally miss this because the injection lives behind the authenticated UI.
STEP 03

Trigger template rendering

The payload is rendered when the message is processed; the GHSA PoC triggers it through the Email settings test flow. That rendering step converts a stored admin-panel change into code-writing behavior on the server. This is where the flaw crosses from dangerous template access into actual server compromise.
Conditions required:
  • A template-render event must occur after the payload is stored
  • Mailer test path or another system-message send path is available
  • The application user can write to the target filesystem or volume
Where this breaks in practice:
  • Some environments restrict outgoing mail features or do not exercise the test path routinely
  • Application write permissions may be limited to non-executable locations
  • Segregated storage and read-only web roots can break the chain here
Detection/coverage: Watch for admin POSTs to system message and email settings endpoints, unexpected mail test invocations, and write activity into served upload paths. EDR/FIM on the web host is much more useful than perimeter IDS here.
STEP 04

Call the dropped webshell for OS command execution

After a PHP file lands in a web-accessible location, the attacker reaches it directly with curl or a browser and executes commands as the web server user. From there, typical follow-on actions are credential theft from .env, database access, persistence, and lateral movement. The GHSA also documents direct secret-disclosure angles via exposed craft.app properties.
Conditions required:
  • Dropped file ends up in a URL-reachable location
  • The web server executes PHP in that location
  • Outbound or inbound controls do not block the follow-on command channel
Where this breaks in practice:
  • Uploads often live on object storage, CDN-backed volumes, or non-PHP-served paths
  • Many hardened web stacks forbid PHP execution in upload directories
  • Even successful RCE usually starts as the low-privilege web user, not immediate root
Detection/coverage: This is the easiest step for defenders to catch: web logs for unusual .php?c= access, process execution from php-fpm/Apache/Nginx workers, EDR child-process alerts, and secret access anomalies.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo public in-the-wild exploitation evidence found in authoritative sources reviewed, and not listed in CISA KEV as of 2026-05-29.
PoC availabilityPublic PoC is effectively available from the GitHub advisory itself, including payloads that write shell.php; reporter credited as mHe4am.
EPSSUser-supplied EPSS is 0.00208, which is very low and aligns with the strong prereq chain. FIRST describes EPSS as a 30-day exploitation probability model via its API/docs.
KEV statusNo. CISA's Known Exploited Vulnerabilities Catalog does not show this CVE in the reviewed data.
CVSS vector reality checkCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H is mathematically high because post-auth RCE is devastating, but PR:H is the whole story operationally: this starts from admin-level compromise, not initial access.
Affected versionsGitHub/NVD list >= 5.0.0-RC1, < 5.9.0-beta.1 and >= 4.0.0-RC1, < 4.17.0-beta.1.
Fixed versionsPatched in 5.9.0-beta.1 and 4.17.0-beta.1. The patch series adds tighter Twig sandbox controls and AllowedInSandbox handling in the Craft codebase.
Config prerequisiteThe published attack prerequisites require an authenticated admin with allowAdminChanges enabled, or access to the System Messages utility. Craft docs recommend setting allowAdminChanges=false in production deployments.
Exposure / scanning signalNo authoritative public GreyNoise/Censys/Shodan measurement specific to this CVE was found in reviewed primary sources. Inference: real exposure tracks externally reachable Craft admin panels, not every public Craft site.
Disclosure / reportingPublished 2026-03-04 in NVD, with the GitHub advisory published 2026-03-02 and reporter attribution to mHe4am.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (6.4/10)

The decisive factor is attacker position: exploitation begins only after an attacker already holds Craft administrative access or equivalent utility access. That turns this from an initial-compromise emergency into a high-impact post-auth escalation path on a relatively narrow population of hardened CMS hosts.

HIGH Exploit mechanics and affected/fixed version ranges
HIGH Downgrade driven by admin-authentication prerequisite
MEDIUM Real-world exposure prevalence across enterprise Craft deployments

Why this verdict

  • Start from 9.1, then subtract for PR:H: this requires authenticated administrator access or equivalent utility permissions, which implies the attacker has already crossed your identity and control-plane defenses.
  • Subtract again for population narrowing: only Craft CMS versions in the 4.x/5.x affected bands matter, and not every enterprise even runs Craft at meaningful scale.
  • Subtract for production hardening friction: Craft's own docs recommend allowAdminChanges=false in production, which removes the easiest attack path in mature deployments.
  • Subtract for chain complexity after auth: successful weaponization still needs a writable web-accessible filesystem or volume plus a render trigger, so raw impact is broader than real reachability.
  • Add back some severity for host compromise impact: if the chain lands, the attacker can execute commands, expose .env secrets, and pivot from the web tier.

Why not higher?

This is not unauthenticated remote code execution, and it is not a broad one-click admin browser bug. The chain assumes prior compromise of an administrator or delegated privileged user, and many production deployments break the path with allowAdminChanges=false, non-executable upload paths, or restricted admin exposure.

Why not lower?

Once the prerequisites are met, the outcome is real server-side code execution and secret disclosure, not a cosmetic admin-panel issue. For organizations that do expose Craft admin interfaces or run looser production configs, this can become the cleanest path from CMS-admin compromise to web-tier takeover.

05 · Compensating Control

What to do — in priority order.

  1. Disable admin changes in production — Set allowAdminChanges=false anywhere it is operationally viable. This directly breaks the most straightforward attack prerequisite, and because this is a MEDIUM reassessment there is no mitigation SLA — implement during your next hardening cycle rather than treating it as emergency change work.
  2. Put /admin behind stronger access controls — Require SSO, MFA, VPN or IP allowlisting for the Craft control panel so the attacker cannot satisfy the PR:H prerequisite cheaply. There is no mitigation SLA — use your normal identity-hardening window, but prioritize any internet-exposed admin surface.
  3. Block PHP execution from writable paths — Ensure upload directories, volumes, and other app-writable locations cannot execute PHP or other server-side code. That severs the PoC's write shell -> browse to shell chain even if a template payload lands; for a MEDIUM finding there is no mitigation SLA, so roll this into your standard web-stack hardening work.
  4. Audit admin role and utility permissions — Review who can reach System Messages, Email settings, and other template-edit surfaces; remove dormant admins and enforce least privilege. There is no mitigation SLA — apply in your normal access-review cadence, but do not leave broad shared admin accounts in place.
  5. Monitor for new executable files in served storage — Use FIM/EDR to alert on .php or unexpected executable content created under web-served storage and uploads. There is no mitigation SLA for this severity bucket, but this control has outsized value because it catches both exploitation and other CMS abuse paths.
What doesn't work
  • A WAF alone does not solve this because the exploit lives behind authenticated admin workflows and uses legitimate application features after login.
  • Email security controls do not help; the render trigger is local application behavior, not a malicious inbound message.
  • Plugin-only patching misses the point; this is in the core craftcms/cms package and tied to Twig sandbox exposure.
  • Relying on low EPSS is not a compensating control; it only says broad exploitation is unlikely, not that a compromised admin cannot weaponize it immediately.
06 · Verification

Crowdsourced verification payload.

Run this on the Craft application host in the application root that contains composer.lock, composer.json, or vendor/craftcms/cms. Invoke it as bash verify-cve-2026-28697.sh /var/www/craft (replace the path with your install root). No root is required; read access to the app directory is enough.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# verify-cve-2026-28697.sh
# Checks whether a local Craft CMS installation is in the vulnerable version range for CVE-2026-28697.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u

APP_ROOT="${1:-.}"

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

find_version() {
  local root="$1"
  local v=""

  # 1) composer.lock
  if [[ -f "$root/composer.lock" ]]; then
    v=$(php -r '
      $f=$argv[1];
      $j=json_decode(file_get_contents($f), true);
      if (!is_array($j)) exit(1);
      foreach (["packages","packages-dev"] as $bucket) {
        if (!empty($j[$bucket]) && is_array($j[$bucket])) {
          foreach ($j[$bucket] as $pkg) {
            if (($pkg["name"] ?? "") === "craftcms/cms") {
              $ver = $pkg["version"] ?? "";
              $ver = preg_replace("/^v/", "", $ver);
              echo $ver;
              exit(0);
            }
          }
        }
      }
      exit(1);
    ' "$root/composer.lock" 2>/dev/null || true)
    if [[ -n "$v" ]]; then
      echo "$v"
      return 0
    fi
  fi

  # 2) vendor package composer.json
  if [[ -f "$root/vendor/craftcms/cms/composer.json" ]]; then
    v=$(php -r '
      $f=$argv[1];
      $j=json_decode(file_get_contents($f), true);
      $ver = $j["version"] ?? "";
      $ver = preg_replace("/^v/", "", $ver);
      echo $ver;
    ' "$root/vendor/craftcms/cms/composer.json" 2>/dev/null || true)
    if [[ -n "$v" ]]; then
      echo "$v"
      return 0
    fi
  fi

  # 3) composer show fallback
  if command -v composer >/dev/null 2>&1 && [[ -f "$root/composer.json" ]]; then
    v=$(cd "$root" && composer show craftcms/cms --format=json 2>/dev/null | php -r '
      $j=json_decode(stream_get_contents(STDIN), true);
      $ver = $j["versions"][0] ?? ($j["version"] ?? "");
      $ver = preg_replace("/^v/", "", $ver);
      echo $ver;
    ' 2>/dev/null || true)
    if [[ -n "$v" ]]; then
      echo "$v"
      return 0
    fi
  fi

  return 1
}

VERSION="$(find_version "$APP_ROOT" || true)"

if [[ -z "$VERSION" ]]; then
  echo "UNKNOWN - could not determine installed craftcms/cms version"
  exit 2
fi

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

  $isVuln = false;

  # Affected ranges per GHSA/NVD:
  # >= 4.0.0-RC1, < 4.17.0-beta.1
  # >= 5.0.0-RC1, < 5.9.0-beta.1
  if (version_compare($v, "4.0.0-RC1", ">=") && version_compare($v, "4.17.0-beta.1", "<")) {
      $isVuln = true;
  }
  if (version_compare($v, "5.0.0-RC1", ">=") && version_compare($v, "5.9.0-beta.1", "<")) {
      $isVuln = true;
  }

  echo $isVuln ? "VULNERABLE" : "PATCHED";
' "$VERSION" 2>/dev/null || true)

if [[ "$RESULT" == "VULNERABLE" ]]; then
  echo "VULNERABLE - craftcms/cms $VERSION is in the affected range for CVE-2026-28697"
  exit 1
elif [[ "$RESULT" == "PATCHED" ]]; then
  echo "PATCHED - craftcms/cms $VERSION is outside the affected range for CVE-2026-28697"
  exit 0
else
  echo "UNKNOWN - version comparison failed for detected version: $VERSION"
  exit 2
fi
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, pull an inventory of every craftcms/cms instance, confirm whether any production sites still allow admin changes, and identify which admin panels are internet-reachable. For this MEDIUM reassessment there is no noisgate mitigation SLA — go straight to the 365-day remediation window; schedule upgrades to fixed versions within the noisgate remediation SLA of ≤365 days, and fold hardening actions like allowAdminChanges=false, stronger /admin access controls, and non-executable upload paths into your next normal change cycle rather than treating this as an all-hands emergency.

Sources

  1. NVD CVE-2026-28697
  2. GitHub Security Advisory GHSA-v47q-jxvr-p68x
  3. Craft CMS fix commit 9dc2a4a
  4. Craft docs: `allowAdminChanges`
  5. Craft docs: Email / System Messages / Twig sandbox
  6. CISA Known Exploited Vulnerabilities Catalog
  7. FIRST EPSS API documentation
  8. MITRE CWE-1336
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.