Skip to content

GHSA-vwg3-w8w3-pc79 CVE-2026-62230 CVE-2026-62673

Grav: .htaccess file extension rules bypass via case variation on case-insensitive filesystems

High Packagist
Severity
0.0 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.54%
Chance of exploitation in the next 30 days. Higher than 45.30% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-178 Improper Handling of Case Sensitivity
Fix
2.0.4
Upgrade getgrav/grav to this version or later

Description

Summary

The default .htaccess shipped with Grav (and the reference webserver-configs/htaccess.txt) contains security rules that block direct HTTP access to sensitive file types (.yaml, .yml, .php, .json, .twig, etc.) under user/ and system/vendor/ directories. However, these rules lack the [NC] (No Case) flag, making them case-sensitive. On case-insensitive filesystems (Windows/NTFS, macOS/HFS+, or Linux with Docker volumes mounted from Windows/macOS), an attacker can bypass these rules by requesting files with uppercase extensions (e.g., .YAML, .PHP, .JSON).

Affected Versions

  • Grav 2.0.1 (latest stable as of June 2026) — confirmed
  • Grav 1.7.x — likely affected (same .htaccess rules)
  • All versions shipping the current webserver-configs/htaccess.txt

Affected Component

File: .htaccess (root of Grav installation) Reference: webserver-configs/htaccess.txt

Affected Rules (lines 68, 70, 72)

# Line 68 — system/vendor file types
RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|htm|shtml|shtm|json|yaml|yml|php|php2|php3|php4|php5|phar|phtml|pl|py|cgi|twig|sh|bat)$ error [F]

# Line 70 — user file types
RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|php2|php3|php4|php5|phar|phtml|pl|py|cgi|twig|sh|bat)$ error [F]

# Line 72 — .md files globally
RewriteRule \.md$ error [F]

All three rules use [F] without [NC], making the extension match case-sensitive.

Steps to Reproduce

  1. Install Grav on a system with a case-insensitive filesystem:

    • Windows (native WAMP/XAMPP)
    • macOS (default HFS+)
    • Docker on Windows/macOS with volume mounts (e.g., ./data:/var/www/html)
  2. Create or use any plugin that stores sensitive data in its YAML config (e.g., API keys):

    user/plugins/my-plugin/my-plugin.yaml
    
  3. Request the file with a case-varied extension:

    GET /user/plugins/my-plugin/my-plugin.YAML HTTP/1.1
    
  4. Expected: HTTP 403 Forbidden

  5. Actual: HTTP 200 OK — full file contents returned, including any API keys or sensitive configuration

Impact

  • Information disclosure: Plugin configuration files (.yaml) containing API keys, credentials, or sensitive settings can be read by unauthenticated users
  • Source code exposure: PHP source files can be downloaded (instead of executed) when requested with .PHP extension on some configurations
  • Configuration exposure: user/config/system.yaml, user/config/site.yaml, and other system configuration files are accessible

Fix

Add the [NC] flag to the three affected rules:

RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|htm|shtml|shtm|json|yaml|yml|php|php2|php3|php4|php5|phar|phtml|pl|py|cgi|twig|sh|bat)$ error [F,NC]
RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|php2|php3|php4|php5|phar|phtml|pl|py|cgi|twig|sh|bat)$ error [F,NC]
RewriteRule \.md$ error [F,NC]

The [NC] flag makes the extension matching case-insensitive, covering .YAML, .Yaml, .PHP, .Json, etc.

Mitigating Factors

  • On native Linux with ext4 filesystem (case-sensitive), the attack does not work because Apache cannot resolve the uppercase filename to the actual file
  • Grav 2.0's Twig sandbox blocks access to plugins config subtree from page content, preventing SSTI-based config exfiltration
  • The user/accounts/, user/config/, and user/data/ folders have separate rules (line 62, 66) that block ALL file types regardless of extension — these are not affected

Environment

  • Grav: 2.0.1
  • PHP: 8.3
  • Apache: 2.4 with mod_rewrite
  • OS: Docker (php:8.3-apache) with volume mounted from Windows 10 (NTFS)
  • Tested: June 2026

Reporter

Sisnetic

Affected versions

Packagist

PackageVulnerableFixed in
getgrav/grav
< 2.0.4
2.0.4

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

getgrav/grav Packagist → 2.0.4

composer
composer require getgrav/grav:^2.0.4

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
Present
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
High
Integrity (vulnerable system)
None
Availability (vulnerable system)
None
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

CVSS 3.1 7.5

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Weakness

CWE-178: Improper Handling of Case Sensitivity

The product does not properly account for differences in case sensitivity when accessing or determining the properties of a resource, leading to inconsistent results.

How to prevent it
  • Architecture and Design: Avoid making decisions based on names of resources (e.g. files) if those resources can have alternate names.
  • Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
  • Implementation: Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass allowlist validation schemes by introducing dangerous inputs after they have been checked.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-xjw5-q542-3vmr
Grav: config_denied_paths default list omits `system`, exposing real secrets (e.g. system.cache.redis.password) via the Twig sandbox when config_access is enabled
High Also affects getgrav/grav
GHSA-3jhr-mxmx-38cx
Grav: UserInterface offsetget/offsetexists allow-listed in Twig sandbox let editor-authored content leak hashed_password and 2FA secrets via offsetGet()
Medium Also affects getgrav/grav
GHSA-9ccq-2jfg-qw33
Grav: Origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match
Critical Also affects getgrav/grav
GHSA-38p6-h87p-r4cg
Grav: Non constant time nonce comparison in Utils::verifyNonce() used for CSRF protection
Medium Also affects getgrav/grav
GHSA-p597-crqc-m349
Grav: The system, site, and theme Twig variables bypass the content sandbox entirely and are never covered by config_denied_paths
Medium Also affects getgrav/grav
GHSA-47ch-6w46-6xm7
Grav: media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content
Medium Also affects getgrav/grav
GHSA-jq29-c7v8-rg55
Grav: Path Traversal in MediaUploadTrait::deleteFile() Allows Arbitrary File Deletion
High Also affects getgrav/grav
GHSA-8hgv-xc77-jmcr
Grav: Page editors can inject arbitrary script into rendered pages via the Twig sandbox's assets.addJs/addCss allowlist, escalating to super-admin
Medium Also affects getgrav/grav
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor