Aug 7, 2026·8 min read·13 visits
Craft CMS local storage driver validates path containment before normalizing the path. This allows attackers to bypass boundary checks using protocol-prefixed traversal sequences, resulting in unauthorized local file access.
A path traversal vulnerability exists in the local filesystem driver of Craft CMS. Due to validation occurring before path normalization, directory containment checks can be bypassed by utilizing specific protocol schemes like 'file://' along with directory traversal sequences. This allows authenticated users with administrative privileges to access or manipulate files outside the defined storage root directory.
The local filesystem driver (Local.php) in Craft CMS is responsible for managing the physical storage and manipulation of assets within configured local storage volumes. It exposes an attack surface through asset management capabilities, where users can upload, index, or rename physical resources. To maintain security boundaries, Craft CMS enforces a directory containment validation mechanism to prevent users from referencing files outside of their designated storage root.
This vulnerability is characterized as an improper limitation of a pathname to a restricted directory, corresponding to CWE-22 and CWE-23. The flaw occurs because of a logical desynchronization between how paths are validated and how they are subsequently normalized before execution. This order-of-operations vulnerability allows directory containment validation to be bypassed completely when specific inputs are processed.
An attacker with administrative privileges or permission to modify asset configurations can submit paths that contain URI protocol schemes such as file:// alongside directory traversal sequences. Because the application evaluates security boundaries on the un-normalized string and then processes the normalized output, the safety guarantees of the filesystem driver are invalidated. This results in arbitrary file read and write operations depending on the downstream function executing the path.
The core issue is a validation-then-normalization vulnerability within the prefixPath method of src/fs/Local.php. When generating absolute file system paths, the application attempts to verify that the target path does not escape the storage volume root. This validation is delegated to Path::ensurePathIsContained(). Crucially, the application performs this validation check on the raw, uncanonicalized path input before any normalization takes place.
The helper method Path::ensurePathIsContained() calculates directory nesting levels by splitting the path into segments using standard separators (/ and \). It iterates through these segments, incrementing an integer counter for normal folders and decrementing it for parent directory markers (..). If this counter drops below zero, the method concludes that the path has traversed past the root directory and returns false. This simple state machine is effective for relative paths but fails when dealing with unexpected protocol prefixes.
When a path starts with a protocol scheme like file://, the validator splits the prefix into segments, such as file:. The validator treats file: as a regular subdirectory, which increments the internal depth counter. However, when the string is later processed by FileHelper::normalizePath(), a regular expression replaces the protocol prefix file:// with an empty string. The subsequent directory traversal segments (../) are then evaluated during the final canonicalization process, moving the directory depth higher than the root directory.
Because the protocol prefix artificially increases the validation depth, the directory traversal escapes the root directory without triggering the depth counter limit. The containment check succeeds, the protocol prefix is stripped during normalization, and the final absolute path resolved by the PHP engine points to local files outside the storage volume. This represents a classic validation bypass due to incorrect state tracking on desanitized parameters.
To illustrate the vulnerability, we examine the original implementation of the prefixPath method in src/fs/Local.php before the patch:
protected function prefixPath(string $path = ''): string
{
// Step 1: Validate containment on raw path input
if (!Path::ensurePathIsContained($path)) {
throw new FsException("The path `$path` is not contained.");
}
// Step 2: Normalize the path and append to the root directory
return $this->getRootPath() . DIRECTORY_SEPARATOR . FileHelper::normalizePath($path);
}The validator splits the input on forward and backward slashes. Consider the payload a/b/c/d/file://../../../../etc/passwd. The parsed segment list contains ['a', 'b', 'c', 'd', 'file:', '..', '..', '..', '..', 'etc', 'passwd']. The containment check processes these segments, raising the depth counter to five before encountering four parent directory indicators. The final depth counter never falls below zero, and validation succeeds. Next, the string is passed to FileHelper::normalizePath() which executes a regular expression clean-up:
public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR): string
{
// Strip file protocol wrappers
$path = preg_replace('/^(file:\\/\\/)*/i', '', $path);
...
$path = parent::normalizePath($path, $ds);
return $path;
}The regular expression strips out the file:// string, leaving a/b/c/d/../../../../etc/passwd. The underlying parent class resolves the parent directory markers, collapsing the string to ../etc/passwd. The function returns this relative path to prefixPath(), which concatenates it with the root path, producing /var/www/web/uploads/../etc/passwd. This path escapes the restricted boundary.
The vulnerability was corrected by changing the order of operations in src/fs/Local.php to ensure the input is normalized first:
protected function prefixPath(string $path = ''): string
{
// Step 1: Normalize the path first
$path = FileHelper::normalizePath($path);
// Step 2: Perform containment checks on the normalized path
if (!Path::ensurePathIsContained($path)) {
throw new FsException("The path `$path` is not contained.");
}
// Step 3: Use the verified normalized path
return $this->getRootPath() . DIRECTORY_SEPARATOR . $path;
}In the patched version, the input a/b/c/d/file://../../../../etc/passwd is normalized to ../etc/passwd first. When passed to the validator, the initial segment .. immediately reduces the depth counter to -1, resulting in a validation failure and throwing an FsException. This fix is logically sound and completely addresses the desynchronization bug.
Exploitation of this vulnerability requires the attacker to have administrative or specific privileged access within Craft CMS. The attacker must target asset-management endpoints or configurations that accept user-controlled path parameters, such as the asset indexing tool or folders associated with local volumes. Because these tools translate user configurations into physical path resolutions, they act as the execution interface for the underlying filesystem driver.
To construct an exploit payload, the attacker must calculate the necessary depth of the target directory structure. If the volume root is located at /var/www/web/storage/user_uploads, the attacker needs to traverse four directory levels upward to reach the system root. To do this, the attacker creates a payload containing four directory padding segments, a file:// scheme, and four .. traversal sequences.
An example payload is formulated as follows:
folder1/folder2/folder3/folder4/file://../../../../etc/passwdWhen processed by the vulnerable application, the validation function treats folder1 through folder4 and the file: segment as positive depth markers, keeping the overall state positive. After normalization, the padding folders are eliminated by the traversal sequences, leaving only the relative traversal to /etc/passwd. The application subsequently executes the file operation against the system file rather than a subdirectory of the volume root, exposing the target content to the administrative panel.
The concrete security impact of this vulnerability depends on the specific actions performed by the downstream components that call the local filesystem driver. If the path traversal is triggered within a read context, such as downloading or indexing assets, the attacker can read arbitrary files from the local filesystem that are readable by the PHP process. This includes administrative configurations, environmental files, database credentials, and system credentials.
In write contexts, such as renaming or moving files, the vulnerability could allow an attacker to overwrite system files or write files to writable web directories. An attacker could potentially upload a PHP web shell into the public web root by traversing out of the asset storage folder and writing into a publicly accessible directory. This turns the path traversal bypass into an arbitrary code execution vector.
This vulnerability is tracked under GitHub Security Advisory GHSA-7HXC-F267-H5Q7. The vendor has assigned a low-severity rating due to the prerequisite requirement of administrative privileges. However, in environments utilizing multi-tenant setups or delegated administration, this flaw represents an escalation path that compromises the host server. The estimated CVSS score is 4.9, with the vector string CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N reflecting the high privilege requirement.
Remediation requires upgrading Craft CMS core to a secure version. The development team has released patches for the affected branches. Organizations running Craft CMS 4.x must update to version 4.18.2 or later. Organizations running Craft CMS 5.x must update to version 5.10.6 or later. Upgrades are applied through PHP Composer by running the core package update command:
composer update craftcms/cmsTo detect potential exploitation attempts, security operations teams should analyze server request logs and application error logs. Look for instances of administrative actions containing protocol indicators like file:// or file%3A%2F%2F in combination with directory traversal patterns like ../. Additionally, monitor application crash logs for craft\errors\FsException which indicate that a path containment validation failed.
System administrators should implement system-level hardening to limit the impact of directory traversal vulnerabilities. Ensure the web server and PHP-FPM processes run under a low-privileged dedicated user account. This user must be restricted from reading critical system files like /etc/passwd or accessing other directories outside of the active web root. Implementing restricted PHP configurations such as setting open_basedir can also prevent the PHP engine from accessing files outside specified paths.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Craft CMS Craft CMS | >= 4.0.0, < 4.18.2 | 4.18.2 |
Craft CMS Craft CMS | >= 5.0.0, < 5.10.6 | 5.10.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS Score | 4.9 |
| Exploit Status | PoC available |
| CISA KEV | Not Listed |
| Ransomware Use | No |
The software 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 restricted directory.
An uncontrolled resource consumption vulnerability exists in the Docling document conversion library. Maliciously structured HTML, JATS, ODS, or BoxNote inputs containing table cells with excessively large 'rowspan' or 'colspan' attribute values trigger algorithmic complexity conditions. This allows unauthenticated remote attackers to initiate resource exhaustion states, crashing or hanging the target document processing pipeline while bypassing configured timeouts.
A Local File Inclusion (LFI) and Arbitrary File Disclosure vulnerability exists in Docling and Docling Slim versions >= 2.16.0 up to 2.131.0. When parsing serialized DoclingDocument structures using the JSON input format, the backend fails to restrict image URI schemes, allowing remote attackers to retrieve local files and verify path existence on the host system during embedded document export.
Docling, a tool for parsing and processing diverse document formats, is vulnerable to arbitrary file read, arbitrary file write, and potential remote code execution (RCE) in versions 2.94.0 through 2.131.0. The vulnerability occurs when applications configure Docling to use the Tectonic engine for rendering TikZ diagrams into images. Because the compilation did not restrict hazardous TeX primitives or sandbox the environment, an attacker can supply crafted documents containing malicious TikZ definitions to access or modify local files and execute arbitrary commands under the privileges of the processing application.
An SSRF guard bypass vulnerability in the Docling document conversion engine allows unauthenticated attackers to bypass internal IP access controls. The vulnerability exists due to a DNS rebinding Time-of-Check Time-of-Use (TOCTOU) condition, URL authority parsing inconsistencies, and unvalidated network requests triggered during headless browser page rendering.
A technical analysis of CVE-2026-105742 (GHSA-p3fw-7699-7926), a sensitive information disclosure vulnerability in the Docling document processing library. Vulnerable versions of Docling indiscriminately forward custom HTTP headers, such as authentication tokens, to arbitrary third-party origins and during cross-origin redirects while fetching remote image assets from untrusted HTML and EPUB documents.
CVE-2026-106121 is a Denial of Service (DoS) vulnerability in the RabbitMQ Java Client library (amqp-client) affecting versions prior to 5.37.0. The vulnerability resides in the legacy, custom JSON-RPC parsing class com.rabbitmq.tools.json.JSONReader. When parsing malformed or truncated payloads ending within a quoted string or single-line comment, the parser's scanner enters an infinite loop. This occurs because the loop lacks an exit condition for the end-of-input sentinel character returned by the iterator, leading to either CPU exhaustion or a JVM crash from an OutOfMemoryError.