GHSA-GWFR-JFJF-92VV
Vulnerability from github – Published: 2026-05-05 21:29 – Updated: 2026-05-08 15:36Insecure 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.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "getgrav/grav"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.0.0-beta.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-7317"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-502"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-05T21:29:29Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "# Insecure Deserialization in File Cache\n\n- **Severity:** High \n- **CWE:** CWE-502\n- **Location:** `system/src/Grav/Framework/Cache/Adapter/FileCache.php`\n- **Sink:** `unserialize($value, [\u0027allowed_classes\u0027 =\u003e true])`\n\n## Affected version(s)\n\n- **Affected:** `\u003e= 1.7.44` and `\u003c= 1.7.49.5` (verified in current codebase and changelog-covered releases).\n- **Fixed:** No upstream fix identified in the reviewed branch at the time of analysis.\n- **Notes:** Earlier `1.7.x` releases may also be affected, but were not fully back-traced in this review.\n\n## Notes\n`allowed_classes =\u003e true` allows object instantiation and does not constrain classes.\n\n## PoC (Primitive Demonstration)\n\n### Preconditions\n- Local PHP runtime.\n- Goal is to validate the deserialization primitive used in cache retrieval.\n\n### Steps\n```bash\nphp -r \u0027\nclass CacheWakeup { public function __wakeup(){ file_put_contents(\"/tmp/grav_filecache_poc.txt\", \"wakeup\"); } }\n\n$payload = serialize(new CacheWakeup());\nunserialize($payload, [\"allowed_classes\" =\u003e true]);\n\necho file_exists(\"/tmp/grav_filecache_poc.txt\") ? \"FILECACHE_UNSERIALIZE_TRIGGERED\\n\" : \"FILECACHE_UNSERIALIZE_NOT_TRIGGERED\\n\";\n\u0027\n```\n\n### Expected Result\n- Output contains: `FILECACHE_UNSERIALIZE_TRIGGERED`.\n\n### Interpretation\nThis reproduces the same unsafe primitive used by `FileCache::doGet()`:\n`unserialize($value, [\u0027allowed_classes\u0027 =\u003e true])`.\nIf cache files are attacker-tampered, object magic methods may execute.\n\n## Exploit Preconditions\n- Cache file poisoning/tampering capability.\n\n## Recommendation\n- Avoid object deserialization in cache payloads.\n- Use non-object formats and integrity protection for cache files.\n\n\n\n---\n\n## Maintainer note \u2014 fix applied (2026-04-24)\n\nFixed in Grav core on the `2.0` branch: commit [`c66dfeb5f`](https://github.com/getgrav/grav/commit/c66dfeb5f) \u2014 will ship in **2.0.0-beta.2**.\n\n**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:\n\n```\nv2\n\u003cexpires\u003e\n\u003ckey\u003e\n\u003chmac-hex\u003e\n\u003cserialized\u003e\n```\n\nExisting caches rebuild transparently on first read. Note that `Framework\\Cache\\Adapter\\FileCache` isn\u0027t wired into Grav\u0027s main cache path \u2014 Symfony\u0027s `FilesystemAdapter` is \u2014 but the class is reachable by plugin and downstream consumers, so the hardening applies defensively.\n\n**Files:**\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- [`tests/unit/Grav/Common/Security/FileCacheSecurityTest.php`](https://github.com/getgrav/grav/blob/2.0/tests/unit/Grav/Common/Security/FileCacheSecurityTest.php) \u2014 round-trip, tampered-payload rejection, wrong-key forgery rejection, pre-v2 file rebuild, key-field mismatch.",
"id": "GHSA-gwfr-jfjf-92vv",
"modified": "2026-05-08T15:36:36Z",
"published": "2026-05-05T21:29:29Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/security/advisories/GHSA-gwfr-jfjf-92vv"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-7317"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/5a12f9be8314682c8713e569e330f11805d0a663"
},
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/commit/c66dfeb5f"
},
{
"type": "WEB",
"url": "https://github.com/devsamuelsantiago/grav-cms-filecache-object-injection"
},
{
"type": "PACKAGE",
"url": "https://github.com/getgrav/grav"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/798732"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/359965"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/359965/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "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",
"type": "CVSS_V4"
}
],
"summary": "Grav has Insecure Deserialization in File Cache"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.