← Back to Feed CACHED · 2026-10-21 18:30:46 · CACHE_KEY CVE-2026-46633
CVE-2026-46633 · CWE-94 · Disclosed 2026-07-14

Twig template engine PHP code injection via {% use %} tag name single-quote breakout

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like a bank vault with a back door that only opens if a customer is allowed to etch their own nameplate

CVE-2026-46633 is a code-injection flaw in Twig (the default PHP template engine for Symfony) prior to version 3.26.0. The Compiler::string() method escapes double quotes, dollar signs, backslashes, NUL, and TAB — but not single quotes. When a template name from a {% use %} tag is compiled via subcompile() → string() and placed inside a PHP single-quoted literal in ModuleNode::compileConstructor(), a name containing a ' terminates the literal early. The attacker's payload becomes arbitrary PHP that executes the first time the compiled cache file loads. Critically, SecurityPolicy allows {% use %} unconditionally regardless of allowedTags, so this bypasses the Twig sandbox entirely — meaning even applications that thought they safely sandboxed user-authored templates are exposed. Affected versions: twig/twig < 3.26.0. Fixed in 3.26.0 (Debian bookworm backport: 3.5.1-1+deb12u3; trixie: 3.27.0-0+deb13u1).

The vendor rated this CVSS 9.8 CRITICAL (AV:N/AC:L/PR:N/UI:N), implying zero-click, unauthenticated, network-reachable RCE. That score is overstated for the vast majority of deployments. The chain requires the attacker to control the *template name string* passed to {% use %}, not just template *content*. In standard Symfony/Drupal/CraftCMS setups, {% use %} arguments are hardcoded by developers — user input never reaches them. The realistic attack surface is limited to two scenarios: (1) CMS or multi-tenant platforms that let authenticated users author Twig templates inside a sandbox (CraftCMS template editors, Drupal modules with Twig authoring, custom email-template builders) — here the sandbox bypass is clean and reliable, but requires at minimum an authenticated content-editor role, making the true vector PR:L not PR:N; and (2) bespoke applications that dynamically construct {% use %} names from request parameters, which is architecturally unusual. The vendor score treats the worst theoretical case as the baseline. Reality narrows this to a HIGH-severity sandbox escape that matters most for CMS operators.

"Sandbox bypass to RCE is real, but most Twig deployments never pass user input as a template name."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Attain template-name influence

The attacker needs to inject a crafted string into the template-name argument of a {% use %} tag. In the sandbox-bypass scenario, this means the attacker has authenticated access to a CMS template editor (CraftCMS, Drupal Twig modules, custom newsletter builders) that lets them write raw Twig source. In the rarer unauthenticated scenario, the application must dynamically construct the {% use %} name from request parameters — e.g. {% use request.get('layout') %}.
Conditions required:
  • Authenticated CMS access with template-editing privileges OR application passes untrusted input as a {% use %} template name
Where this breaks in practice:
  • Standard Symfony apps hardcode {% use %} names in version-controlled .html.twig files — no user input path exists
  • CMS platforms gate template editing behind admin or editor roles, requiring credential compromise first
  • The {% use %} tag is far less common than {% include %} or {% extends %} — many codebases have zero {% use %} instances
Detection/coverage: Nessus plugins 316066 and 318004 (deployed 2026-05-22) detect vulnerable twig/twig versions via Composer lockfile inspection.
STEP 02

Craft a single-quote breakout payload

The attacker authors a template containing {% use "payload'.system('id').'" %} (or similar). The single quote inside the name breaks out of the PHP single-quoted literal that ModuleNode::compileConstructor() generates, injecting an arbitrary PHP expression. The SecurityPolicy check does not inspect {% use %} arguments, so this is allowed even when the sandbox is enabled.
Conditions required:
  • Knowledge of the Twig compiler's string-escaping gap (public since GHSA-7p85-w9px-jpjp disclosure on 2026-05-20)
Where this breaks in practice:
  • No public weaponized PoC exists yet — the attacker must independently craft the breakout payload from the advisory description
  • Applications with a WAF inspecting template content may block raw single-quote sequences in POST bodies
Detection/coverage: WAF rules matching single-quote injection patterns inside Twig tag syntax ({% use) can flag attempts. No Nuclei or Sigma template observed as of 2026-10-22.
STEP 03

Trigger template compilation

Twig compiles templates to PHP cache files on first load (or after cache invalidation). Once the attacker submits the malicious template, the next HTTP request that renders it — or a cache-warmup event — triggers compilation. The injected PHP expression is written into the cache file as executable code.
Conditions required:
  • The Twig environment has caching enabled (default in production)
  • The template is reachable via a route or included by another template
Where this breaks in practice:
  • If file-system caching is disabled (rare in production, common in dev), compiled output goes to eval() which is still exploitable but may trigger disable_functions restrictions
  • Some deployments use read-only container filesystems that prevent cache writes, though Twig typically falls back to in-memory compilation
Detection/coverage: File integrity monitoring (FIM) on the Twig cache directory can detect unexpected PHP file writes.
STEP 04

Achieve remote code execution

When the compiled cache file is loaded by PHP's require or include, the injected expression executes with the privileges of the PHP-FPM worker or web server user (typically www-data). The attacker now has RCE on the application server, can read environment variables (database credentials, API keys), pivot to connected datastores, or establish persistence.
Conditions required:
  • PHP runtime does not have aggressive disable_functions or open_basedir restrictions that prevent the injected function from executing
Where this breaks in practice:
  • Modern container deployments often run PHP with restricted open_basedir and disable_functions lists that block system(), exec(), passthru() — the attacker must find an unrestricted function
  • Hardened PHP configurations (Suhosin, Snuffleupagus) can block dynamic code execution patterns
Detection/coverage: EDR/HIDS on the web server detecting unusual child processes spawned by PHP-FPM (e.g. sh, curl, wget). RASP solutions like Sqreen or Datadog ASM can detect code-injection at runtime.
03 · Compensating Control

1
HIGH 7.5→IGNORE 0.0
SEVERITY REDUCED
Upgrade twig/twig to ≥3.26.0 immediately — The definitive fix. Run composer require twig/twig:^3.26.0 and test. For Debian, apply DSA-6320-1 (bookworm) or DSA-6311-1 (trixie). Under the noisgate mitigation SLA for HIGH, deploy the compensating control within 30 days; under the noisgate remediation SLA, complete full patching within 180 days. Given the straightforward Composer update path, most teams should be able to patch well within 30 days.
2
HIGH 7.5→LOW 2.5
SEVERITY REDUCED
Audit and remove user-controlled template name paths — Search your codebase for {% use %} tags where the template name is derived from user input, request parameters, or database values. Replace dynamic names with a whitelist of allowed template names. This eliminates the primary exploitation prerequisite even without patching. Deploy within 30 days per the noisgate mitigation SLA for HIGH.
3
HIGH 7.5→MEDIUM 5.5
SEVERITY REDUCED
Restrict template authoring to trusted roles only — In CMS platforms (CraftCMS, Drupal), ensure that Twig template editing is gated behind admin-level roles, not content-editor roles. This raises the attacker's required privilege level from editor to admin, significantly increasing friction. Review role assignments and remove template-editing permissions from untrusted user roles within 30 days.
4
HIGH 7.5→MEDIUM 5.0
SEVERITY REDUCED
Harden PHP runtime with disable_functions and open_basedir — Configure php.ini to disable dangerous functions: disable_functions = system,exec,passthru,shell_exec,popen,proc_open,pcntl_exec. Set open_basedir to the application root. This does not prevent the code injection but severely limits what the injected code can do, converting RCE to a constrained information-disclosure or DoS scenario. Deploy within 30 days.
5
HIGH 7.5→HIGH 6.5
Deploy WAF rules blocking single-quote injection in Twig tags — Add a WAF rule (ModSecurity, Cloudflare WAF, AWS WAF) that inspects POST bodies and template-upload endpoints for patterns like {% use followed by unescaped single quotes. This is a fragile defense (bypassable with encoding tricks) but raises the bar for opportunistic exploitation. Deploy within 7 days as an interim measure.
What doesn't work
  • Twig sandbox (SecurityPolicy) — the vulnerability exists precisely because {% use %} is allowed unconditionally by the sandbox. Configuring allowedTags does not help; {% use %} is not subject to tag filtering.
  • CSP headers — Content Security Policy operates at the browser layer and has no effect on server-side PHP code injection.
  • Network segmentation alone — while limiting lateral movement post-exploitation, it does nothing to prevent the initial code injection, which occurs within the PHP process on the application server itself.
  • Upgrading only Symfony — the vulnerability is in twig/twig, not the Symfony framework package. Updating symfony/symfony without updating twig/twig leaves the flaw in place.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNone observed. Not listed in CISA KEV. No GreyNoise tags or Shadowserver scanning campaigns detected as of 2026-10-22. No threat actor campaigns reported.
Proof-of-ConceptNo public PoC. pocindex.io returns no results. No GitHub repos named CVE-2026-46633. Feedly noted a possible Gist reference in social media but no confirmed weaponized exploit. The advisory description provides enough detail for a skilled attacker to reconstruct the payload independently.
EPSS0.00688 (0.69%) — 59th percentile. Indicates low predicted exploitation probability within 30 days. Consistent with the high friction required for real-world exploitation.
CISA KEVNot listed as of 2026-10-22.
CVSS VectorVendor: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. GHSA scored CVSS 4.0 at 9.3. The PR:N and AC:L assumptions are overstated — realistic exploitation requires either authenticated CMS access (PR:L) or an unusual application architecture (AC:H). A more accurate CVSS 3.1 vector for the sandbox-bypass scenario: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8.
Affected Versionstwig/twig < 3.26.0 — all 3.x releases prior to the fix. Twig 2.x reached EOL in 2023 and likely contains the same flaw but will not receive a patch.
Fixed Versionstwig/twig 3.26.0+ (upstream). Debian bookworm: 3.5.1-1+deb12u3 (DSA-6320-1). Debian trixie: 3.27.0-0+deb13u1 (DSA-6311-1). Debian sid: 3.26.0-1.
Installed Base501M+ Composer installs, 7,280 dependent packages on Packagist. Twig is the default template engine for Symfony (23% of PHP framework market) and used by Drupal 8+, CraftCMS, Matomo, phpBB, OroCRM, and others.
Disclosure & CreditsDisclosed 2026-05-20 (GHSA), 2026-07-14 (NVD publish). Reported by Anvil Secure in collaboration with Claude and Anthropic Research. Coordinated disclosure through Symfony security process.
Scanner CoverageNessus plugins 316066 and 318004 (2026-05-22). GitHub Dependabot alerts on GHSA-7p85-w9px-jpjp. No Nuclei template or Sigma rule observed. Composer audit command flags the advisory.

Sources.

  1. GitHub Advisory GHSA-7p85-w9px-jpjp
  2. Symfony Security Blog — CVE-2026-46633
  3. Twig 3.26.0 Release Notes
  4. Debian Security Tracker — CVE-2026-46633
  5. Feedly CVE Intelligence — CVE-2026-46633
  6. Packagist — twig/twig
  7. SentinelOne Vulnerability Database — CVE-2026-46633
  8. CraftCMS Prior Sandbox Bypass — GHSA-vfcw-xv8p-8rj2
05 · The Call

Final Verdict
↓ DOWNGRADED to HIGH (7.5/10)

Why this verdict

  • Template-name control is the critical gate. The entire chain requires the attacker to inject a payload into the *name argument* of a {% use %} tag — not the template body. In standard Symfony/Drupal deployments, {% use %} arguments are hardcoded in developer-authored .html.twig files under version control. User input never reaches this code path. This single prerequisite eliminates the vast majority of the 500M+ installs from the exploitable population.
  • Sandbox bypass is the realistic scenario, but requires authentication. The most plausible attack vector is a CMS or multi-tenant platform (CraftCMS, Drupal with Twig authoring modules, custom email-template editors) where authenticated users can author templates. The sandbox bypass via {% use %} is clean and reliable here, but the attacker needs at minimum content-editor credentials — making the true CVSS PR:L, not PR:N. This drops the realistic CVSS from 9.8 to ~8.8 before other friction.
  • No exploitation evidence compounds the downward pressure. Zero public PoCs, zero KEV listing, zero GreyNoise/Shadowserver scanning activity, and an EPSS of 0.69% (59th percentile) all indicate this is not being weaponized. The advisory has been public for five months with no confirmed exploitation.
  • Role multiplier: Web application template engine. Twig is not a canonically high-value-role component (not an IdP, hypervisor, PAM, backup, kernel agent, or network edge appliance). It runs inside PHP web applications. When exploited, the blast radius is *host-level* — the attacker gains the PHP-FPM worker's privileges (typically www-data), with lateral movement potential to connected databases and internal networks. In CMS deployments hosting PII or e-commerce data, this is serious but bounded to the application tier. The blast radius does not inherently reach domain/fleet/supply-chain scale without additional pivoting. Estimated fraction of Twig installs in sandbox/CMS template-authoring scenarios: ~10-15% of production deployments. This exceeds the 1% floor threshold for HIGH but does not reach the canonical high-value-role threshold for a CRITICAL floor.
  • AC:L is defensible only for the sandbox-bypass sub-population. For the ~85-90% of Twig deployments where template names are hardcoded, the vulnerability is effectively unexploitable (AC:H at best, IGNORE in practice). The HIGH verdict reflects the weighted reality: a meaningful subset of deployments faces a reliable sandbox escape to RCE, but the supermajority is unaffected.

Why not higher?

CRITICAL would require either (a) a broadly reachable unauthenticated RCE affecting most deployments or (b) the affected component being canonically in a high-value role. Neither condition holds. The unauthenticated scenario (dynamic {% use %} names from request parameters) is architecturally abnormal — no major framework or CMS constructs {% use %} this way by default. The sandbox-bypass scenario requires authentication. With no PoC, no exploitation, and 0.69% EPSS after five months of disclosure, elevating to CRITICAL overstates the operational risk for most Twig consumers.

Why not lower?

MEDIUM would understate the risk for the non-trivial population of CMS and multi-tenant platforms that use Twig's sandbox. CraftCMS has an established history of Twig sandbox bypass CVEs being exploited (GHSA-vfcw-xv8p-8rj2), demonstrating that this attack surface is actively researched and targeted. The sandbox bypass is clean — {% use %} is unconditionally allowed by SecurityPolicy — requiring no complex gadget chains. Any CMS operator who relies on Twig sandboxing for security isolation faces a direct escalation to RCE. Twig's 500M+ install base and 7,280 dependents mean even a 10% exposure rate represents tens of thousands of vulnerable applications.

06 · Verification

Crowdsourced verification payload.

Run this script on any host where a PHP application with Composer is deployed. Execute from the application's root directory (where composer.json lives). Requires read access to composer.lock. Example: bash check_cve_2026_46633.sh /var/www/myapp

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_46633.sh — Detect CVE-2026-46633 (Twig < 3.26.0)
# Usage: bash check_cve_2026_46633.sh [/path/to/app]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

APP_DIR="${1:-.}"
LOCKFILE="${APP_DIR}/composer.lock"

if [ ! -f "$LOCKFILE" ]; then
  echo "UNKNOWN — composer.lock not found at ${LOCKFILE}"
  exit 2
fi

# Extract twig/twig version from composer.lock
TWIG_VERSION=$(python3 -c "
import json, sys
with open('${LOCKFILE}') as f:
    data = json.load(f)
for pkg in data.get('packages', []) + data.get('packages-dev', []):
    if pkg.get('name') == 'twig/twig':
        print(pkg.get('version', '').lstrip('v'))
        sys.exit(0)
print('')
" 2>/dev/null)

if [ -z "$TWIG_VERSION" ]; then
  echo "UNKNOWN — twig/twig not found in ${LOCKFILE}"
  exit 2
fi

echo "Detected twig/twig version: ${TWIG_VERSION}"

# Compare versions: vulnerable if < 3.26.0
VULN=$(python3 -c "
from packaging.version import Version
try:
    v = Version('${TWIG_VERSION}')
    print('VULNERABLE' if v < Version('3.26.0') else 'PATCHED')
except Exception:
    print('UNKNOWN')
" 2>/dev/null || echo "UNKNOWN")

if [ "$VULN" = "VULNERABLE" ]; then
  echo "VULNERABLE — twig/twig ${TWIG_VERSION} is affected by CVE-2026-46633"
  echo "  Fix: composer require twig/twig:^3.26.0"
  exit 1
elif [ "$VULN" = "PATCHED" ]; then
  echo "PATCHED — twig/twig ${TWIG_VERSION} is not affected"
  exit 0
else
  echo "UNKNOWN — could not parse version '${TWIG_VERSION}'"
  exit 2
fi