VYPR
Critical severityNVD Advisory· Published Sep 13, 2023· Updated Feb 13, 2025

Craft CMS Remote Code Execution vulnerability

CVE-2023-41892

Description

Craft CMS is a platform for creating digital experiences. This is a high-impact, low-complexity attack vector. Users running Craft installations before 4.4.15 are encouraged to update to at least that version to mitigate the issue. This issue has been fixed in Craft CMS 4.4.15.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
craftcms/cmsPackagist
>= 4.0.0-RC1, < 4.4.154.4.15

Affected products

1

Patches

3
a270b928f3d3

Take two

https://github.com/craftcms/cmsbrandonkellyJun 28, 2023via ghsa
1 file changed · +1 1
  • src/controllers/ConditionsController.php+1 1 modified
    @@ -41,7 +41,7 @@ public function beforeAction($action): bool
     
             $this->requireCpRequest();
     
    -        $baseConfig = Json::decodeIfJson($this->request->getBodyParam('config'));
    +        $baseConfig = Component::cleanseConfig(Json::decodeIfJson($this->request->getBodyParam('config')));
             $config = Component::cleanseConfig($this->request->getBodyParam($baseConfig['name']));
             $newRuleType = ArrayHelper::remove($config, 'new-rule-type');
             $conditionsService = Craft::$app->getConditions();
    
7359d18d4638

Fixed an RCE vulnerability

https://github.com/craftcms/cmsbrandonkellyJun 27, 2023via ghsa
4 files changed · +52 1
  • CHANGELOG.md+2 0 modified
    @@ -6,8 +6,10 @@
     - The `up`, `migrate/up`, and `migrate/all` commands now revert any project config changes created by migrations on failure.
     - The `up`, `migrate/up`, and `migrate/all` commands now prompt to restore the backup created at the outset of the command, or recommend restoring a backup, on failure.
     - Added `craft\console\controllers\BackupTrait::restore()`.
    +- Added `craft\helpers\Component::cleanseConfig()`.
     - Fixed a bug where Single entries weren’t getting preloaded for template macros, if the template body wasn‘t rendered. ([#13312](https://github.com/craftcms/cms/issues/13312))
     - Fixed a bug where asset folders could get dynamically created for elements with temporary slugs. ([#13311](https://github.com/craftcms/cms/issues/13311))
    +- Fixed an RCE vulnerability.
     
     ## 4.4.14 - 2023-06-13
     
    
  • src/controllers/ConditionsController.php+2 1 modified
    @@ -11,6 +11,7 @@
     use craft\base\conditions\ConditionInterface;
     use craft\base\conditions\ConditionRuleInterface;
     use craft\helpers\ArrayHelper;
    +use craft\helpers\Component;
     use craft\helpers\Json;
     use craft\web\Controller;
     use Illuminate\Support\Collection;
    @@ -41,7 +42,7 @@ public function beforeAction($action): bool
             $this->requireCpRequest();
     
             $baseConfig = Json::decodeIfJson($this->request->getBodyParam('config'));
    -        $config = $this->request->getBodyParam($baseConfig['name']);
    +        $config = Component::cleanseConfig($this->request->getBodyParam($baseConfig['name']));
             $newRuleType = ArrayHelper::remove($config, 'new-rule-type');
             $conditionsService = Craft::$app->getConditions();
             $this->_condition = $conditionsService->createCondition($config);
    
  • src/helpers/Component.php+21 0 modified
    @@ -81,6 +81,27 @@ public static function validateComponentClass(string $class, ?string $instanceOf
             return true;
         }
     
    +    /**
    +     * Cleanses a component config of any `on X` or `as X` keys.
    +     *
    +     * @param array $config
    +     * @return array
    +     * @since 4.4.15
    +     */
    +    public static function cleanseConfig(array $config): array
    +    {
    +        foreach ($config as $key => $value) {
    +            if (is_string($key) && (str_starts_with($key, 'on ') || str_starts_with($key, 'as '))) {
    +                unset($config[$key]);
    +                continue;
    +            }
    +            if (is_array($value)) {
    +                $config[$key] = static::cleanseConfig($value);
    +            }
    +        }
    +        return $config;
    +    }
    +
         /**
          * Instantiates and populates a component, and ensures that it is an instance of a given interface.
          *
    
  • tests/unit/helpers/ComponentHelperTest.php+27 0 modified
    @@ -115,6 +115,16 @@ public function testIconSvg(string $needle, ?string $icon, string $label): void
             self::assertStringContainsString($needle, Component::iconSvg($icon, $label));
         }
     
    +    /**
    +     * @dataProvider cleanseConfigDataProvider
    +     * @param array $expected
    +     * @param array $config
    +     */
    +    public function testCleanseConfig(array $expected, array $config)
    +    {
    +        self::assertSame($expected, Component::cleanseConfig($config));
    +    }
    +
         /**
          * @return array
          */
    @@ -304,4 +314,21 @@ public function iconSvgDataProvider(): array
                 'aria-hidden' => ['aria-hidden="true"', '<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>', 'Default'],
             ];
         }
    +
    +    /**
    +     * @return array
    +     */
    +    public function cleanseConfigDataProvider(): array
    +    {
    +        return [
    +            [
    +                ['f' => 'foo', 'b' => 'bar'],
    +                ['f' => 'foo', 'b' => 'bar', 'as f' => 'f', 'on b' => 'b'],
    +            ],
    +            [
    +                ['nested' => ['f' => 'foo', 'b' => 'bar']],
    +                ['nested' => ['f' => 'foo', 'b' => 'bar', 'as f' => 'f', 'on b' => 'b']],
    +            ],
    +        ];
    +    }
     }
    
c0a37e15cc92

Call beforeAction() up front

https://github.com/craftcms/cmsbrandonkellyJun 27, 2023via ghsa
20 files changed · +105 22
  • src/controllers/AssetIndexesController.php+5 1 modified
    @@ -36,11 +36,15 @@ class AssetIndexesController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // No permission no bueno
             $this->requirePermission('utility:asset-indexes');
             $this->requireAcceptsJson();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/AssetSettingsController.php+5 1 modified
    @@ -25,10 +25,14 @@ class AssetSettingsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All user settings actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/BaseElementsController.php+5 1 modified
    @@ -27,10 +27,14 @@ abstract class BaseElementsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All actions require control panel requests
             $this->requireCpRequest();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/ConditionsController.php+7 1 modified
    @@ -34,6 +34,12 @@ class ConditionsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
    +        $this->requireCpRequest();
    +
             $baseConfig = Json::decodeIfJson($this->request->getBodyParam('config'));
             $config = $this->request->getBodyParam($baseConfig['name']);
             $newRuleType = ArrayHelper::remove($config, 'new-rule-type');
    @@ -48,7 +54,7 @@ public function beforeAction($action): bool
                 $this->_condition->addConditionRule($rule);
             }
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/FieldsController.php+5 1 modified
    @@ -38,10 +38,14 @@ class FieldsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All field actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         // Groups
    
  • src/controllers/FsController.php+5 1 modified
    @@ -33,10 +33,14 @@ class FsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All asset volume actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/GraphqlController.php+7 3 modified
    @@ -59,13 +59,17 @@ public function beforeAction($action): bool
                 throw new NotFoundHttpException(Craft::t('yii', 'Page not found.'));
             }
     
    -        Craft::$app->requireEdition(Craft::Pro);
    -
             if ($action->id === 'api') {
                 $this->enableCsrfValidation = false;
             }
     
    -        return parent::beforeAction($action);
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
    +        Craft::$app->requireEdition(Craft::Pro);
    +
    +        return true;
         }
     
         /**
    
  • src/controllers/ImageTransformsController.php+5 1 modified
    @@ -31,10 +31,14 @@ class ImageTransformsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All image transform actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/LivePreviewController.php+5 1 modified
    @@ -36,12 +36,16 @@ class LivePreviewController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // Mark this as a Live Preview request
             if ($action->id === 'preview') {
                 $this->request->setIsLivePreview(true);
             }
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/PluginsController.php+5 1 modified
    @@ -28,10 +28,14 @@ class PluginsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All plugin actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/PluginStoreController.php+5 1 modified
    @@ -35,10 +35,14 @@ class PluginStoreController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All plugin store actions require an admin
             $this->requireAdmin(false);
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/RebrandController.php+6 1 modified
    @@ -35,8 +35,13 @@ class RebrandController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             Craft::$app->requireEdition(Craft::Pro);
    -        return parent::beforeAction($action);
    +
    +        return true;
         }
     
         /**
    
  • src/controllers/RoutesController.php+5 1 modified
    @@ -26,10 +26,14 @@ class RoutesController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All route actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/SectionsController.php+5 1 modified
    @@ -36,10 +36,14 @@ class SectionsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All section actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/SitesController.php+5 1 modified
    @@ -36,10 +36,14 @@ class SitesController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All actions require an admin account
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/StructuresController.php+5 1 modified
    @@ -41,6 +41,10 @@ class StructuresController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             $this->requirePostRequest();
             $this->requireAcceptsJson();
     
    @@ -80,7 +84,7 @@ public function beforeAction($action): bool
                 throw new NotFoundHttpException('Element not found');
             }
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/SystemMessagesController.php+5 1 modified
    @@ -27,12 +27,16 @@ class SystemMessagesController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             Craft::$app->requireEdition(Craft::Pro);
     
             // Make sure they have access to the System Messages utility
             $this->requirePermission('utility:system-messages');
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/SystemSettingsController.php+5 1 modified
    @@ -40,10 +40,14 @@ class SystemSettingsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All system setting actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/UserSettingsController.php+5 1 modified
    @@ -28,14 +28,18 @@ class UserSettingsController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All user settings actions require an admin
             $this->requireAdmin();
     
             if ($action->id !== 'save-user-settings') {
                 Craft::$app->requireEdition(Craft::Pro);
             }
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    
  • src/controllers/VolumesController.php+5 1 modified
    @@ -35,10 +35,14 @@ class VolumesController extends Controller
          */
         public function beforeAction($action): bool
         {
    +        if (!parent::beforeAction($action)) {
    +            return false;
    +        }
    +
             // All asset volume actions require an admin
             $this->requireAdmin();
     
    -        return parent::beforeAction($action);
    +        return true;
         }
     
         /**
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.