See how phalcon compares to other vendors in security performance
Phalcon is a high-performance, full-stack PHP framework. Prior to 5.15.0, every Phalcon MVC application built with a default router registers a built-in route whose compiled PCRE pattern contains the nested quantifier (/.), and the same construct is produced by the /:params placeholder and the CLI router. Phalcon\Mvc\Router::handle() matches this pattern against the attacker-controlled request URI on every request, so a crafted path such as one containing repeated slashes followed by decoded newlines can trigger catastrophic backtracking and cause CPU exhaustion or route-matching failure. This issue is fixed in version 5.15.0.
Phalcon is a high-performance, full-stack PHP framework. Prior to 5.14.1, Phalcon\Encryption\Crypt::decrypt compares the attacker-supplied HMAC tag against the freshly computed HMAC using PHP/Zephir identity comparison, which lowers to a byte-wise comparison that returns early on the first differing byte. This observable timing discrepancy can allow an attacker to recover a valid tag byte-by-byte and attach it to a chosen IV and ciphertext so that decrypt() accepts tampered encrypted content as authentic. This issue is fixed in version 5.14.1.
Summary
The Volt template compiler in Phalcon generates the PHP for the join filter by string-concatenating the filter's raw template-literal argument bytes with no escaping. The separator literal is dropped verbatim between two single quotes the compiler emits, and the piped array argument is emitted completely bare. A Volt template whose join arguments are attacker-influenced can therefore break out of the generated join('…') call and inject arbitrary PHP into the compiled template. Volt writes that compiled template to a cache file and require()s it at render time, so the injected PHP executes i.e. compile-time PHP code injection (server-side template injection -> remote code execution) for any application that compiles attacker-controlled Volt source.
Details
Root cause
phalcon/Mvc/View/Engine/Volt/Compiler.zep:2544-2546:
zephir case "join": return "join('" . funcArguments[1]["expr"]["value"] . "', " . funcArguments[0]["expr"]["value"] . ")";
funcArguments[1]["expr"]["value"] (the separator) and funcArguments[0]["expr"]["value"] (the piped array) are the raw values of the parsed template tokens. Unlike every other expression in the compiler, they are not routed through expression() and receive no escaping: the separator value is spliced verbatim inside the join(' … ' quotes with no neutralisation of ', and the array value is emitted with no quoting at all. Volt's scanner stores string-literal bytes verbatim (escape sequences are not decoded), so attacker bytes survive intact into the generated PHP.
Generated-C ground truth -> build/phalcon/phalcon.zep.c (Phalcon 5.15.0):
c ZEPHIRCONCATSVSVS(returnvalue, "join('", &19$$24, "', ", &22$$24, ")");
i.e. literally "join('" + separator + "', " + array + ")" with both attacker-controlled fragments unescaped.
The compiled output is then written to a cache file and required by Phalcon\Mvc\View\Engine\Volt::render(), so any PHP spliced in by the attacker runs at render time.
PoC
php <?php use Phalcon\Mvc\View\Engine\Volt\Compiler;
$cmd = 'id; uname -a; hostname';
$b64 = base64encode($cmd); $tpl = "{{ ['x'] | join(\"',[]); echo shellexec(base64decode('$b64')); //\") }}";
$compiled = (new Compiler())->compileString($tpl);
$f = tempnam(sysgettempdir(), 'volt') . '.php'; fileputcontents($f, $compiled); include $f; unlink($f);
<img width="1226" height="386" alt="image" src="https://github.com/user-attachments/assets/4d5da3f4-0bc9-41d9-b741-13c9ea9b08fe" />
Impact
Where an application compiles Volt source that is wholly or partly attacker-controlled, this yields remote code execution in the web-server process.