getgrav/grav · Security
Grav has Insecure Deserialization in File Cache
- GHSA ID
- GHSA-gwfr-jfjf-92vv
- CVE ID
- CVE-2026-7317
- Severity
- low
- Published
- Updated
- CVSS
- 5 · CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L
- CVSS v3
- 5 · CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L
- CVSS v4
- 1.3 · CVSS:4.0/AV:N/AC:H/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P
Description
Insecure Deserialization in File Cache
- Severity: High
- CWE: CWE-502
- Location:
system/src/Grav/Framework/Cache/Adapter/FileCache.php - Sink:
unserialize($value, ['allowed_classes' => true])
Affected version(s)
- Affected:
>= 1.7.44and<= 1.7.49.5(verified in current codebase and changelog-covered releases). - Fixed: No upstream fix identified in the reviewed branch at the time of analysis.
- Notes: Earlier
1.7.xreleases may also be affected, but were not fully back-traced in this review.
Notes
allowed_classes => true allows object instantiation and does not constrain classes.
PoC (Primitive Demonstration)
Preconditions
- Local PHP runtime.
- Goal is to validate the deserialization primitive used in cache retrieval.
Steps
php -r '
class CacheWakeup { public function __wakeup(){ file_put_contents("/tmp/grav_filecache_poc.txt", "wakeup"); } }
$payload = serialize(new CacheWakeup());
unserialize($payload, ["allowed_classes" => true]);
echo file_exists("/tmp/grav_filecache_poc.txt") ? "FILECACHE_UNSERIALIZE_TRIGGERED\n" : "FILECACHE_UNSERIALIZE_NOT_TRIGGERED\n";
'
Expected Result
- Output contains:
FILECACHE_UNSERIALIZE_TRIGGERED.
Interpretation
This reproduces the same unsafe primitive used by FileCache::doGet():
unserialize($value, ['allowed_classes' => true]).
If cache files are attacker-tampered, object magic methods may execute.
Exploit Preconditions
- Cache file poisoning/tampering capability.
Recommendation
- Avoid object deserialization in cache payloads.
- Use non-object formats and integrity protection for cache files.
Maintainer note — fix applied (2026-04-24)
Fixed in Grav core on the 2.0 branch: commit c66dfeb5f — will ship in 2.0.0-beta.2.
What changed: Framework\Cache\Adapter\FileCache now HMAC-signs every cache payload with Security::getNonceKey() on write, and verifies the HMAC on read. Tampered, forged, or pre-upgrade files are treated as cache misses and unlinked instead of being unserialized. The on-disk format is now versioned:
v2
<expires>
<key>
<hmac-hex>
<serialized>
Existing caches rebuild transparently on first read. Note that Framework\Cache\Adapter\FileCache isn't wired into Grav's main cache path — Symfony's FilesystemAdapter is — but the class is reachable by plugin and downstream consumers, so the hardening applies defensively.
Files:
system/src/Grav/Framework/Cache/Adapter/FileCache.php.tests/unit/Grav/Common/Security/FileCacheSecurityTest.php— round-trip, tampered-payload rejection, wrong-key forgery rejection, pre-v2 file rebuild, key-field mismatch.
CWEs
- CWE-20 · Improper Input Validation
- CWE-502 · Deserialization of Untrusted Data
References
- https://github.com/getgrav/grav/security/advisories/GHSA-gwfr-jfjf-92vv ↗
- https://github.com/getgrav/grav/commit/5a12f9be8314682c8713e569e330f11805d0a663 ↗
- https://nvd.nist.gov/vuln/detail/CVE-2026-7317 ↗
- https://github.com/getgrav/grav/commit/c66dfeb5f ↗
- https://github.com/devsamuelsantiago/grav-cms-filecache-object-injection ↗
- https://vuldb.com/submit/798732 ↗
- https://vuldb.com/vuln/359965 ↗
- https://vuldb.com/vuln/359965/cti ↗
- https://github.com/advisories/GHSA-gwfr-jfjf-92vv ↗