Sep 18, 2026·7 min read·23 visits
An authenticated path traversal vulnerability in Grav CMS before 2.0.16 allows media administrators to delete arbitrary files on the system by bypassing incomplete directory validation checks in the MediaUploadTrait.
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.
Grav CMS is a flat-file content management system that relies on structured filesystem directories rather than database backends. In versions prior to 2.0.16, the system handles media-related transactions via functions defined in the MediaUploadTrait class. These operations are exposed through the administrative panel and through custom third-party plugins implementing the MediaUploadInterface structure.
The core vulnerability is classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). The validation routines fail to neutralize directory traversal sequences in user-controlled file deletion parameters. An attacker can manipulate input values to escape the root directory configured for media files, allowing operations to interact with other filesystem areas.
Because the administrative control panel allows authenticated users to upload and manage media files, this vulnerability is accessible to users with media management privileges. Although upstream controllers sometimes sanitize file parameters, direct trait calls inside custom extensions and default administrative deletion actions lack validation containment.
This flaw allows authenticated users with media management permissions to delete system configuration files, environmental variables, or security policy definitions. Removing critical system configuration files like security.yaml or .htaccess can alter application security postures, leading to application denial of service or opening paths to execute arbitrary code.
The technical root cause of CVE-2026-72695 resides in system/src/Grav/Common/Media/Traits/MediaUploadTrait.php inside the deleteFile() method. This method accepts a user-controlled $filename string and attempts to validate it before initiating the filesystem deletion sequence.
To perform validation, the system utilizes the static method Utils::checkFilename() against the isolated filename. However, before the check is executed, the code uses a filesystem utility to extract the base name of the path:
$basename = $filesystem->basename($filename);
if (!Utils::checkFilename($basename)) {
throw new RuntimeException(...);
}If an attacker inputs a traversal string such as ../../../../config/security.yaml, the filesystem helper extracts only the last segment of the path, which is security.yaml. Because security.yaml contains no restricted directory traversal sequences or invalid characters, Utils::checkFilename() validates the string and returns true.
The critical error is that the original, unvalidated $filename variable containing the directory traversal sequence is preserved and used in the actual file system call. This path parameter is forwarded downstream to doRemove($filename, $path), where it is concatenated directly with the target media root path and processed using PHP's native unlink() function. The operating system resolves the traversal indicators, allowing the deletion of files outside of the media root.
The vulnerable code implementation of the deleteFile method processed user input with insufficient checks before performing file operations. Below is the vulnerable segment of code:
// Vulnerable implementation in MediaUploadTrait.php
public function deleteFile(string $filename, ?array $settings = null): void
{
$settings = $this->getUploadSettings($settings);
$filesystem = Filesystem::getInstance(false);
// The application extracts only the basename for validation
$basename = $filesystem->basename($filename);
if (!Utils::checkFilename($basename)) {
throw new RuntimeException($this->translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename, 400);
}
$path = $settings['destination'] ?? $this->getPath();
// The original unsanitized $filename is passed directly to the deletion handler
$this->doRemove($filename, $path);
}To mitigate this issue, the patch introduced the checkFilepath() helper function. This function analyzes the full directory path to ensure that traversal operations are identified and rejected:
// Patched helper function in MediaUploadTrait.php
protected function checkFilepath(string $filename): bool
{
return $filename !== ''
&& strpbrk($filename, "\\\0") === false
&& !str_starts_with($filename, '/')
&& !in_array('..', explode('/', $filename), true);
}The helper performs four sequential validation operations on the input string:
strpbrk to block backslash characters and null byte injection attempts....The deleteFile() method was updated to implement this validation step, checking the integrity of both the path structure and the basename:
// Patched implementation in deleteFile()
$basename = $filesystem->basename($filename);
if (!$this->checkFilepath($filename) || !Utils::checkFilename($basename)) {
throw new RuntimeException($this->translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename, 400);
}This implementation closes the validation gap. It ensures that any input containing traversal components is blocked before reaching the deletion mechanism. Similar checks were also applied to file upload and renaming pathways in the trait.
Exploitation of CVE-2026-72695 requires an authenticated user session with media management permissions. The attack cannot be carried out by unauthenticated users under default configurations. This limitation narrows the attack surface to malicious insiders or attackers who have compromised lower-privileged administrative accounts.
The attack begins when the threat actor identifies a media deletion API endpoint. Instead of providing a standard filename inside the target media folder, the attacker crafts an HTTP request containing relative path indicators. The payload targeting a configuration file looks like this:
POST /admin/media/delete HTTP/1.1
Host: target-grav-site.local
Content-Type: application/json
Authorization: Bearer <valid_token>
{
"filename": "../../../../user/config/security.yaml"
}When the backend processes this request, the vulnerable deleteFile method parses the payload. The extraction component isolates security.yaml and verifies its validity. The method then executes the file removal, causing PHP to resolve the relative traversal indicators relative to the media folder. This resolves to the absolute system path /var/www/html/user/config/security.yaml and deletes the file.
The physical delete operation succeeds because the web server daemon has write permissions on the flat-file configuration directories. The system configuration is deleted, leaving the application in an unconfigured state or disabling specific security controls.
The primary impact of this vulnerability is arbitrary file deletion on the target filesystem. Because Grav CMS uses configuration files to define permissions, routing, and access controls, the deletion of key configuration components can severely disrupt system operations.
Deleting files such as user/config/security.yaml removes defined security policies, which can result in privilege escalation or the bypass of administrative restrictions. If an attacker deletes access control configurations like .htaccess, the web server may expose internal directories or execute files that were previously restricted. This exposure can be leveraged to achieve remote code execution.
According to the Common Vulnerability Scoring System (CVSS v3.1), the vulnerability has a base score of 8.1. The vector string is CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H. This reflects high impacts on both integrity and availability, balanced by the requirement for authenticated low-privilege access.
The Exploit Prediction Scoring System (EPSS) score is 0.00567, indicating a relatively low near-term likelihood of active exploitation in the wild. This is due to the authentication requirement and the absence of publicly released weaponized exploitation frameworks.
The primary mitigation for this vulnerability is upgrading Grav CMS to version 2.0.16 or newer. The update incorporates the checkFilepath() validation helper across all media manipulation interfaces within MediaUploadTrait to prevent path traversal.
Administrators who cannot apply updates immediately should implement system-level mitigations. Directory and file permissions must be audited to ensure that the web server user does not have write access to critical configuration directories. This prevents the deletion of configuration files even if a path traversal bypass is attempted.
Web Application Firewall (WAF) rule sets can be deployed to monitor inbound traffic for traversal sequences. Filtering parameters for structures containing ../ or encoded variations like ..%2f directed toward media management paths can help block exploitation attempts.
Software developers working with similar systems should ensure that input validation is applied to full logical paths rather than isolated file components. Relying on basename extraction for validation is an insufficient control when the original, unsanitized input is passed to filesystem interfaces.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Grav CMS getgrav | < 2.0.16 | 2.0.16 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22: Improper Limitation of a Pathname to a Restricted Directory |
| Attack Vector | Network (Unauthenticated: No, Requires authenticated media management privileges) |
| CVSS v3.1 Score | 8.1 (High) |
| EPSS Score | 0.00567 (Percentile: 45.53%) |
| Impact | Arbitrary File Deletion / Denial of Service / Remote Code Execution |
| Exploit Status | Proof-of-Concept (PoC) |
| KEV Status | Not Listed in CISA KEV Catalog |
The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize sequences such as '..' that can resolve to a location outside of the directory.
CVE-2026-118777 is a prototype pollution vulnerability (CWE-1321) in the Forge JavaScript cryptography and networking library (distributed on npm as node-forge) prior to version 1.4.1. The flaw resides in the forge.http client subsystem, where incoming HTTP cookie names and paths parsed from Set-Cookie response headers are stored directly into standard JavaScript objects without key validation or property isolation. By returning crafted Set-Cookie headers containing keys such as __proto__ or constructor, a malicious HTTP server can inject arbitrary properties into Object.prototype, attach attributes to the global Object constructor, or delete built-in prototype methods to cause an application-wide denial of service.
CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.
CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).
CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.
An algorithmic complexity vulnerability (CWE-407) in pyasn1 versions prior to 0.6.5 allows unauthenticated attackers to cause a Denial of Service (DoS) via crafted ASN.1 payloads. The flaw resides in the binary-encoded ASN.1 REAL mantissa deserialization routine within RealPayloadDecoder.valueDecoder. By performing iterative byte-string slicing and repeated bitwise shifts on a growing arbitrary-precision integer, the decoder exhibits quadratic time and memory allocation complexity relative to the mantissa length. Supplying a BER-, CER-, or DER-encoded payload with a large REAL mantissa causes excessive CPU consumption and blocks application worker processes.
A vulnerability classified under CWE-407 (Inefficient Algorithmic Complexity) exists in the pyasn1 library prior to version 0.6.5. When parsing constructed ASN.1 BIT STRING payloads under BER, CER, or schema-driven DER encoding rules, the BitStringPayloadDecoder repeatedly shifts and copies the entire accumulated bit value for each incoming fragment. Processing inputs with a large number of small constructed fragments induces quadratic CPU consumption, allowing unauthenticated remote attackers to cause a denial of service via resource exhaustion during the initial decoding phase.