GHSA-vj3m-2g9h-vm4p: Grav has multiple RCE vectors Unsafe Unserialize (x3), Command Injection Vulnerability
CRITICALGHSA-vj3m-2g9h-vm4p is a CRITICAL unsafe unserialize (x3), command injection vulnerability (CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')) affecting Grav has multiple RCE vectors libraries in the Packagist ecosystem. The issue involves unsafe unserialize (x3), command injection in git clone, ssti blocklist bypass. Remediation is available by upgrading affected packages to version 2.0.0-beta.2.
GHSA-vj3m-2g9h-vm4p at a Glance
How to Fix GHSA-vj3m-2g9h-vm4p
To remediate this vulnerability, upgrade all affected Grav has multiple RCE vectors dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade getgrav/grav to 2.0.0-beta.2
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for getgrav/grav
How to Check if You Are Affected
To determine whether your application is affected by GHSA-vj3m-2g9h-vm4p, inspect your project dependencies for Grav has multiple RCE vectors:
inspect dependencies for getgrav/grav
Analyze the terminal output to locate any version of Grav has multiple RCE vectors in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.0.0-beta.2), 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-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'), where the product constructs all or part of an os command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended os command when it is sent to a downstream component. In Grav has multiple RCE vectors, 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
GHSA-vj3m-2g9h-vm4p Affected Grav has multiple RCE vectors Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
getgrav/grav
|
Packagist |
< 2.0.0-beta.2
|
2.0.0-beta.2
|
References
Frequently Asked Questions
What is GHSA-vj3m-2g9h-vm4p?
Which Grav has multiple RCE vectors versions are affected?
What version fixes GHSA-vj3m-2g9h-vm4p?
Is getgrav/grav affected?
What CWE is associated with GHSA-vj3m-2g9h-vm4p?
How can I check Packagist dependencies for GHSA-vj3m-2g9h-vm4p?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-vj3m-2g9h-vm4p",
"modified" : "2026-05-05T21:29:02Z",
"published" : "2026-05-05T21:29:02Z",
"aliases" : [ ],
"summary" : "Grav has multiple RCE vectors: unsafe unserialize (x3), command injection in git clone, SSTI blocklist bypass",
"details" : "Multiple RCE vectors were found in Grav CMS. Three are critical, two are high.\n\n**1. Unsafe unserialize() in JobQueue — direct RCE gadget (Critical)**\n\n`system/src/Grav/Common/Scheduler/JobQueue.php:465` calls `unserialize(base64_decode(...))` without restricting `allowed_classes`. The `Job` class has `call_user_func_array($this->command, $this->args)` in its execution path, which is a direct gadget chain — inject a serialized `Job` with `command = 'system'` and `args = ['whoami']`.\n\nThe same codebase actually has a `Serializable` trait that correctly restricts classes, so this inconsistency stands out.\n\n**2. Unsafe unserialize() in FileCache — arbitrary class instantiation (Critical)**\n\n`system/src/Grav/Framework/Cache/Adapter/FileCache.php:75` does `unserialize($value, ['allowed_classes' => true])`. That `true` allows instantiation of any class. If an attacker can write to the cache directory (via any file write primitive), they get object injection → RCE.\n\n**3. Unsafe unserialize() in Session (High)**\n\n`system/src/Grav/Common/Session.php:116` — same `allowed_classes => true` pattern on session data. Lower severity since session storage is typically more restricted.\n\n**4. Command injection in git clone (Critical)**\n\n`system/src/Grav/Console/Cli/InstallCommand.php:150` — only `$this->destination` uses `escapeshellarg()`. The `$data['branch']`, `$data['url']`, and `$data['path']` variables go directly into the shell command without escaping. Admin-accessible via plugin/theme installation.\n\n**5. SSTI blocklist bypass (High)**\n\n`system/src/Grav/Common/Security.php:267-286` — `cleanDangerousTwig()` blocks `twig_array_map` and `twig_array_filter` but not `twig_array_reduce`. Also missing `file_get_contents` and `fwrite` from the dangerous function blocklist. An attacker who can inject Twig templates can bypass the security filter.\n\nAll five are independently exploitable. The unserialize issues are the most concerning since they don't require admin access if there's any file write primitive.\n\n— ProScan AppSec | proscan.one\n\n\n---\n\n## Maintainer note — fix applied (2026-04-24)\n\nFixed in Grav core on the `2.0` branch: commit [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) (items #1, #2, #3, #4) and commit [`38685ac25`](https://github.com/getgrav/grav/commit/38685ac25) + [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) (item #5) — ships in **2.0.0-beta.2**.\n\nAll five vectors addressed:\n\n1. **Scheduler\\JobQueue unsafe unserialize** — `serialized_job` now carries a sibling `serialized_job_hmac` signed with `Security::getNonceKey()`. `reconstructJob` refuses to unserialize an item whose HMAC is missing/mismatched and falls through to the safe structured-fields rebuild. A tampered queue file can no longer smuggle a forged `Job` for direct RCE via `Job::exec → call_user_func_array`. \n → [`system/src/Grav/Common/Scheduler/JobQueue.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Scheduler/JobQueue.php)\n\n2. **FileCache unsafe unserialize** — same HMAC-integrity approach; see separate GHSA-gwfr-jfjf-92vv. \n → [`system/src/Grav/Framework/Cache/Adapter/FileCache.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Framework/Cache/Adapter/FileCache.php)\n\n3. **Session::getFlashObject unsafe unserialize** — payload now wrapped in a `v2|<hmac>|<serialized>` envelope; legacy/forged envelopes return null instead of triggering `unserialize`. \n → [`system/src/Grav/Common/Session.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Session.php)\n\n4. **InstallCommand `git clone` shell injection** — `branch`, `url`, and `path` values read from `user/.dependencies` are now passed through `escapeshellarg`, with a `--` separator before url/path to block option-injection (e.g. `--upload-pack=evil`). \n → [`system/src/Grav/Console/Cli/InstallCommand.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Console/Cli/InstallCommand.php)\n\n5. **SSTI blocklist bypass** — `twig_array_reduce` (the specific name called out) plus `twig_array_some` and `twig_array_every` added to `cleanDangerousTwig`'s `CALLABLE_DANGEROUS_NAMES` alongside the existing `twig_array_map`/`filter`. More importantly, the new Twig content sandbox in 2.0.0-beta.2 blocks this class of attack at a different layer — see the sandbox work in [`38685ac25`](https://github.com/getgrav/grav/commit/38685ac25). \n → [`system/src/Grav/Common/Security.php`](https://github.com/getgrav/grav/blob/2.0/system/src/Grav/Common/Security.php)\n\n**Tests:**\n- [`tests/unit/Grav/Common/Security/UnserializeIntegritySecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/UnserializeIntegritySecurityTest.php) — 8 cases covering JobQueue + Session HMAC integrity.\n- [`tests/unit/Grav/Common/Security/FileCacheSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/FileCacheSecurityTest.php).\n- [`tests/unit/Grav/Common/Security/CleanDangerousTwigTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/CleanDangerousTwigTest.php) — new `twig_array_*` entries in `providerCallbackFunctions`.",
"severity" : [ ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "getgrav/grav"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "2.0.0-beta.2"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/getgrav/grav/security/advisories/GHSA-vj3m-2g9h-vm4p"
}, {
"type" : "WEB",
"url" : "https://github.com/getgrav/grav/commit/5a12f9be8314682c8713e569e330f11805d0a663"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/getgrav/grav"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-502", "CWE-78" ],
"severity" : "CRITICAL",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-05T21:29:02Z",
"nvd_published_at" : null
}
}