Sep 18, 2026·7 min read·4 visits
An insecure string containment check in Grav's pre-boot asset server allows unauthenticated remote attackers to read files in sibling directories via path traversal.
An unauthenticated directory traversal vulnerability exists in Grav CMS prior to version 2.0.15. Due to an insecure string-based containment check (str_starts_with) in the pre-boot static asset server, attackers can read files in sibling directories sharing a prefix with the configured asset path when plugin-asset-map.php is enabled.
CVE-2026-74907 is a directory traversal vulnerability discovered in Grav, an open-source flat-file Content Management System (CMS). The vulnerability is situated within the pre-boot static asset serving routine implemented in the primary index.php routing file. This component bypasses the core application initialization cycle to quickly deliver static files registered in an optional configuration.
Because this pre-boot server operates before the core access control libraries of the CMS are fully instantiated, any routing flaw within it exposed a wide attack surface. The root cause is identified as an insecure string containment check (CWE-22) that fails to validate directory-level boundaries when resolving client paths. Unauthenticated attackers can leverage this defect to read arbitrary files from sibling folders that share a base prefix with the configured asset directory.
To be reachable, the vulnerable code path requires a specific, non-default configuration: the presence of an active asset map profile. If this asset map is present, any remote user can target the pre-boot route without prior authentication. The security impact is confined to unauthorized information exposure on the hosting file system.
The vulnerability stems from a logical design flaw where string-based prefix matching is substituted for filesystem boundary verification. When serving static assets, the pre-boot server resolves the absolute file path using the absolute base path on disk. To verify that the requested file resides inside the allowed directory, the code performs a verification using the PHP built-in str_starts_with function.
The string check was written as str_starts_with($realFile, $realBase). Because this check operates strictly on arbitrary characters and is not directory-aware, it treats folder delimiters as normal characters. Consequently, any directory that begins with the exact characters of the base path will satisfy the condition and bypass the containment boundary.
Consider a configuration where the legitimate base path resolves to /var/www/html/grav/assets and an attacker requests a resource residing in /var/www/html/grav/assets-secret/credentials.txt. The string comparison will evaluate /var/www/html/grav/assets-secret/credentials.txt against /var/www/html/grav/assets. Because the characters align sequentially, the check returns true, incorrectly treating a distinct sibling directory as a subdirectory of the allowed asset directory.
The pre-boot asset verification logic in Grav versions prior to 2.0.15 was structured to evaluate input paths as follows:
// Vulnerable routing block in index.php
$filePath = __DIR__ . '/' . ltrim($diskPath, '/') . $relPath;
$realFile = realpath($filePath);
$realBase = realpath(__DIR__ . '/' . ltrim($diskPath, '/'));
if ($realFile && $realBase && str_starts_with($realFile, $realBase) && is_file($realFile)) {
// Serve the file directly
}In this logic, the code retrieves the relative request path and appends it to the base directory path before passing it to realpath(). The function realpath() resolves all symbolic links, relative references, and directory traversal sequences (..). However, if the canonicalized path of a sibling directory starts with the same string prefix as $realBase, the execution bypasses the boundary constraint.
The official fix in version 2.0.15 resolves this issue by ensuring that the base comparison string terminates on a system-defined directory boundary. This is achieved by stripping any existing trailing slashes and appending a single DIRECTORY_SEPARATOR to the base path comparison string:
// Patched logic in index.php (Grav >= 2.0.15)
$filePath = __DIR__ . '/' . ltrim($diskPath, '/') . $relPath;
$realFile = realpath($filePath);
$realBase = realpath(__DIR__ . '/' . ltrim($diskPath, '/'));
// Containment has to end on a directory boundary. A bare
// prefix test also accepts any sibling whose name merely
// extends the base one.
$inBase = $realFile && $realBase
&& str_starts_with($realFile, rtrim($realBase, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR);
if ($inBase && is_file($realFile)) {
$ext = strtolower(pathinfo($realFile, PATHINFO_EXTENSION));
// Serve file safely
}By converting the base path to /var/www/html/grav/assets/ on Unix-like environments, a request traversing into /var/www/html/grav/assets-secret/credentials.txt will fail the validation because the character at the index of the trailing slash is - instead of /. This patch provides robust and complete mitigation against sibling-prefix directory traversal attacks.
To successfully exploit CVE-2026-74907, an attacker must identify a Grav target where the optional static asset-mapping configuration file user/config/plugin-asset-map.php is active. When this file exists, the static asset handler in index.php is executed for matching request prefixes, allowing path validation checks to run.
An attacker begins by scanning for common sibling directories that share a suffix or prefix string on the server's filesystem, such as assets-secure, assets-secret, or assets-backup. Once a potential target directory is identified, the attacker crafts a raw HTTP GET request to the index route containing traversal sequences within the query string mapping parameters.
GET /index.php?_url=../assets-secret/keys.json HTTP/1.1
Host: target-grav-instance.local
Accept: */*
Connection: closeDuring execution, the server combines the relative path input with the default directory path. When realpath() normalizes the path, the traversal sequence resolves to the sibling folder assets-secret. The comparison check evaluates the path and returns true because the absolute target path starts with the literal characters of the base directory. The web application then processes the file and sends its contents back to the requester in the HTTP response body.
The severity of CVE-2026-74907 is limited to the disclosure of sensitive files (Confidentiality). Because the affected asset-serving logic only reads files from the disk and lacks write or delete routines, an attacker cannot modify files (Integrity) or execute code directly on the target machine (Availability).
The CVSS v3.1 base score is 5.9 (Medium), indicating a conditional exploit path. The Attack Complexity is rated as High because the attack requires a specific prerequisite configuration file to be populated and the target files must reside in a sibling directory with a name extending the base path prefix. If these environments exist, the exploitation does not require authentication or user interaction.
If sensitive data such as system configuration backups, credential store files, or development environments are maintained within the targeted prefix-sharing folders, the impact can escalate. Attackers can leverage leaked API keys or database credentials to conduct lateral movement. Current threat intelligence (EPSS score 0.00333) suggests low active utilization in automated exploitation campaigns.
The definitive remediation is to upgrade Grav to version 2.0.15 or later. The security update patches the routing check in index.php to guarantee directory boundary checks. Administrators must ensure that the core file index.php is replaced during the update and that local modifications do not override the patch.
If an immediate upgrade is not feasible, administrators can apply a manual hotfix. Locate the vulnerability block in the root index.php file and modify the validation logic to enforce directory-level checks using the DIRECTORY_SEPARATOR constant as shown in the patch code analysis. Clear the server's cache after applying the patch to enforce the new validation logic.
If the asset mapping feature is not required for production services, the vulnerability can be mitigated by disabling the configuration. Delete or rename the configuration file located at user/config/plugin-asset-map.php. Removing this file prevents the application from entering the vulnerable pre-boot asset handler, bypassing the vulnerable code path entirely.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Grav getgrav | < 2.0.15 | 2.0.15 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 5.9 (Medium) |
| EPSS Score | 0.00333 (Percentile: 26.56%) |
| Impact | Confidentiality (High) |
| Exploit Status | Proof of Concept / Conceptual |
| CISA KEV Status | Not Listed |
The application uses path components without properly validating that they resolve to directories within the intended base path, allowing string containment bypasses.
A path traversal vulnerability exists in Grav CMS versions prior to 2.0.16. The flaw occurs within the file validation mechanisms of the MediaUploadTrait, enabling authenticated users with media management privileges to bypass sandbox limitations. This allows the deletion of arbitrary files on the filesystem, which can result in denial of service or remote code execution.
CVE-2026-75828 is a critical stored cross-site scripting (XSS) vulnerability in the getgrav Grav CMS before version 2.0.15. The vulnerability resides in the detectXss() security filter mechanism, where parser-differential mismatches between the regular-expression-based server-side validation and browser HTML5 tokenization allow authenticated editors to bypass event-handler detection and inject arbitrary JavaScript execution vectors.
An arbitrary file write and remote code execution vulnerability exists in Grav CMS before version 2.0.15. The vulnerability is caused by using an incomplete denylist validation approach for bare PHP functions in the Blueprint dynamic-data compiler, allowing authenticated users with page-editing or blueprint-configuration privileges to execute arbitrary functions such as error_log.
CVE-2026-75834 is a stored Cross-Site Scripting (XSS) vulnerability in Grav CMS core, caused by a design flaw in its input validation wrapper Security::detectXss(). Regular expressions using the PCRE UTF-8 /u modifier fail-open when encountering invalid UTF-8 sequences or when the PCRE JIT stack limit is exhausted, allowing authenticated users with page-editing privileges to save malicious HTML and scripts.
CVE-2026-75837 is a critical privilege escalation vulnerability affecting the Grav Flat-File Content Management System (CMS) in versions prior to 2.0.14. Due to a missing security guard on the access field within the core Flex group blueprint configuration file (system/blueprints/user/group.yaml), a delegated administrative operator can submit a crafted payload to elevate their permissions to super-administrator, which can then be leveraged to achieve remote code execution.
CVE-2026-76461 is a critical, unauthenticated, remotely exploitable SQL Injection (SQLi) vulnerability in the email parsing engine of Cisco AsyncOS Software for Cisco Secure Email Gateway (SEG). An unauthenticated remote attacker can exploit this vulnerability by transmitting a specially crafted email message containing malicious SQL statements directly through an affected gateway.