Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-46640

CVE-2026-46640: Symfony Twig RCE Vulnerability

CVE-2026-46640 is a remote code execution flaw in Symfony Twig that allows attackers to inject raw PHP code through macro expressions. This post explains its impact, affected versions 3.15.0-3.26.0, and mitigation steps.

Published:

CVE-2026-46640 Overview

CVE-2026-46640 is a PHP code injection vulnerability [CWE-94] in Twig, the template language used by Symfony and other PHP frameworks. Versions from 3.15.0 through 3.25.x allow attacker-controlled strings to be concatenated into a MacroReferenceExpression name without identifier validation. The flaw affects the _self.(<string>) syntax and import-alias dynamic attribute syntax. Raw PHP is emitted into the generated template source and executed at template-load time, bypassing the Twig sandbox. The issue was fixed in Twig version 3.26.0.

Critical Impact

Attackers with the ability to influence Twig template content can inject arbitrary PHP that executes when the template is loaded, resulting in full server compromise in sandboxed and multi-tenant environments.

Affected Products

  • Symfony Twig 3.15.0 through 3.25.x
  • PHP applications using Twig for user-influenced or sandboxed templates
  • Symfony framework deployments bundling vulnerable Twig releases

Discovery Timeline

  • 2026-07-14 - CVE-2026-46640 published to NVD
  • 2026-07-16 - Last updated in NVD database

Technical Details for CVE-2026-46640

Vulnerability Analysis

Twig compiles templates into PHP source code before execution. The compiler builds MacroReferenceExpression nodes when it encounters calls such as _self.macroName(...) or alias.macroName(...) where alias refers to an imported template. In vulnerable versions, the attribute name passed to the macro reference is concatenated directly into the generated PHP with a macro_ prefix. When the attribute is a dynamic string expression rather than a literal identifier, the resulting PHP source contains attacker-controlled characters, including quotes and parentheses. Loading the compiled template executes the injected PHP, providing a sandbox bypass path.

Root Cause

The root cause is missing identifier validation in src/ExpressionParser/Infix/DotExpressionParser.php. The parser accepted any ConstantExpression value as a macro name and interpolated it into the compiled template without verifying it matched a valid PHP identifier pattern. Because Twig's sandbox operates on the template AST rather than the emitted PHP, injection at the code-generation stage escapes sandbox restrictions entirely.

Attack Vector

Exploitation requires an attacker to control template source content or a variable used in _self.(<expr>) or an import-alias dynamic attribute expression. This condition applies to platforms that render user-supplied Twig templates, multi-tenant SaaS applications, and CMS integrations that allow limited template editing under sandbox constraints. A crafted attribute string closes the generated PHP call, appends arbitrary statements, and reopens the syntax to keep compilation valid.

php
// Patch: src/ExpressionParser/Infix/DotExpressionParser.php
         if (
             $expr instanceof NameExpression
+            && $attribute instanceof ConstantExpression
+            && \is_string($name = $attribute->getAttribute('value'))
+            && preg_match('#^[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*$#D', $name)
             && (
                 null !== $parser->getImportedSymbol('template', $expr->getAttribute('name'))
-                || '_self' === $expr->getAttribute('name') && $attribute instanceof ConstantExpression
+                || '_self' === $expr->getAttribute('name')
             )
         ) {
-            return new MacroReferenceExpression(new TemplateVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$attribute->getAttribute('value'), $arguments, $expr->getTemplateLine());
+            return new MacroReferenceExpression(new TemplateVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$name, $arguments, $expr->getTemplateLine());
         }

         return new GetAttrExpression($expr, $attribute, $arguments, $type, $lineno, $nullSafe);
// Source: https://github.com/twigphp/Twig/commit/324fa60545694fa6abe85ded9befcb82e1066bc2

The fix adds a preg_match check enforcing a valid PHP identifier before the value is concatenated into the generated macro name.

Detection Methods for CVE-2026-46640

Indicators of Compromise

  • Twig templates containing _self.( followed by a variable or string expression rather than a literal identifier.
  • Compiled Twig cache files under var/cache/ containing unexpected PHP statements outside macro function bodies.
  • Web server processes spawning shells, php subprocesses, or outbound connections shortly after template rendering.
  • Unexpected file writes in web-writable directories originating from php-fpm or the application user.

Detection Strategies

  • Perform static analysis of Twig template repositories for the patterns _self.( and <alias>.( where the attribute is not a bareword identifier.
  • Inspect the Twig compiled cache directory for PHP files where macro_ prefixed function calls contain non-identifier characters.
  • Enable Twig deprecation and error logging and monitor for parse or runtime errors from generated template files.
  • Run software composition analysis to enumerate applications resolving twig/twig at versions between 3.15.0 and 3.25.x.

Monitoring Recommendations

  • Monitor PHP-FPM and web worker processes for anomalous child process creation using endpoint telemetry.
  • Track file integrity of the Twig cache directory and application webroot for unexpected writes.
  • Alert on outbound network connections from PHP processes to previously unseen destinations.

How to Mitigate CVE-2026-46640

Immediate Actions Required

  • Upgrade twig/twig to version 3.26.0 or later across all applications and dependent Symfony deployments.
  • Clear and regenerate the Twig template cache after upgrade to remove any previously compiled vulnerable output.
  • Audit template sources and any datastore that supplies Twig content for _self.( and dynamic import-alias attribute usage.
  • Rotate credentials and secrets accessible to the web application if evidence of exploitation is found.

Patch Information

The fix is available in Twig 3.26.0. See the GitHub Security Advisory GHSA-45vw-wh46-2vx8, the upstream patch commit 324fa605, and the Twig v3.26.0 release notes. Update via Composer to pull the fixed release.

Workarounds

  • Restrict template authoring to trusted users until the upgrade is deployed.
  • Disable features that render user-supplied Twig templates in sandbox mode, as the sandbox does not contain this issue.
  • Add a repository pre-commit or CI check that rejects templates containing _self.( or <alias>.( with non-identifier attribute values.
bash
# Upgrade Twig to the patched release
composer require "twig/twig:^3.26.0"

# Clear the compiled template cache (Symfony example)
php bin/console cache:pool:clear cache.global_clearer
rm -rf var/cache/prod/twig var/cache/dev/twig

# Verify the resolved version
composer show twig/twig | grep versions

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.