CVE-2021-27903 (published 2021-06-30T12:15Z)

An issue was discovered in Craft CMS before 3.6.7. In some circumstances, a potential Remote Code Execution vulnerability existed on sites that did not restrict administrative changes (if an attacker were somehow able to hijack an administrator's session).

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-862
cwe_name: Missing Authorization
description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are not applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/craftcms/cms
commit_details: repo_url: https://github.com/craftcms/cms
author: brandonkelly
author_date: 2021-02-16 14:04:31-08:00
msg: Don’t allow pointing a Local volume to a system directory
repository: repo_url: https://github.com/craftcms/cms
repo_name: craftcms/cms
description: Build bespoke content experiences with Craft.
repo_language: PHP
owner: craftcms
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -10,6 +10,9 @@
 - Adjusted GraphQL complexity values for relational fields.
 - Updated Composer to 2.0.9.
 
+### Security
+- It’s no longer possible to save a Local volume with the File System Path setting set to a system directory (e.g. the `templates/` or `vendor/` folders).
+
 ## 3.6.6 - 2021-02-15
 
 ### Added

programming_language: Markdown
filename: Local.php
change_type: ModificationType.MODIFY
diff: @@ -12,6 +12,7 @@ use craft\helpers\FileHelper;
 use League\Flysystem\Adapter\Local as LocalAdapter;
 use League\Flysystem\FileExistsException;
 use League\Flysystem\FileNotFoundException;
+use yii\validators\InlineValidator;
 
 /**
  * The local volume class. Handles the implementation of the local filesystem as a volume in
@@ -58,9 +59,61 @@ class Local extends FlysystemVolume implements LocalVolumeInterface
     {
         $rules = parent::defineRules();
         $rules[] = [['path'], 'required'];
+        $rules[] = [['path'], 'validatePath'];
         return $rules;
     }
 
+    /**
+     * @param string $attribute
+     * @param array|null $params
+     * @param InlineValidator $validator
+     * @param string $path
+     * @return void
+     * @since 3.6.7
+     */
+    public function validatePath(string $attribute, ?array $params, InlineValidator $validator, string $path): void
+    {
+        if ($created = !file_exists($path)) {
+            FileHelper::createDirectory($path);
+        }
+
+        $path = realpath($this->getRootPath());
+
+        if ($path === false) {
+            return;
+        }
+
+        // Make sure it’s not within any of the system directories
+        $pathService = Craft::$app->getPath();
+        $systemDirs = [
+            Craft::getAlias('@contentMigrations'),
+            Craft::getAlias('@lib'),
+            $pathService->getComposerBackupsPath(false),
+            $pathService->getConfigBackupPath(false),
+            $pathService->getConfigDeltaPath(false),
+            $pathService->getConfigPath(),
+            $pathService->getDbBackupPath(false),
+            $pathService->getLogPath(false),
+            $pathService->getRebrandPath(false),
+            $pathService->getRuntimePath(false),
+            $pathService->getSiteTemplatesPath(),
+            $pathService->getSiteTranslationsPath(),
+            $pathService->getTestsPath(),
+            $pathService->getVendorPath(),
+        ];
+
+        foreach ($systemDirs as $dir) {
+            $dir = realpath($dir);
+            if ($dir !== false && strpos($path . DIRECTORY_SEPARATOR, $dir . DIRECTORY_SEPARATOR) === 0) {
+                $validator->addError($this, $attribute, Craft::t('app', 'Local volumes cannot be located within system directories.'));
+                if ($created) {
+                    FileHelper::removeDirectory($path);
+                }
+                break;
+            }
+        }
+    }
+
     /**
      * @inheritdoc
      */

programming_language: PHP