VYPR
Moderate severityNVD Advisory· Published Mar 4, 2026· Updated Mar 6, 2026

Craft affected by authenticated RCE via Twig SSTI - create() function + Symfony Process gadget

CVE-2026-28695

Description

Craft is a content management system (CMS). There is an authenticated admin RCE in Craft CMS 5.8.21 via Server-Side Template Injection using the create() Twig function combined with a Symfony Process gadget chain. The create() Twig function exposes Craft::createObject(), which allows instantiation of arbitrary PHP classes with constructor arguments. Combined with the bundled symfony/process dependency, this enables RCE. This bypasses the fix implemented for CVE-2025-57811 (patched in 5.8.7). This vulnerability is fixed in 5.9.0-beta.1 and 4.17.0-beta.1.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Authenticated admin RCE in Craft CMS 5.8.21 via SSTI using create() Twig function and Symfony Process gadget chain, bypassing a prior fix.

Vulnerability

Details

CVE-2026-28695 is an authenticated remote code execution vulnerability in Craft CMS versions 5.8.21 and earlier (up to 5.9.0-beta.1) and 4.x prior to 4.17.0-beta.1. The root cause is that the create() Twig function exposes Craft::createObject(), which allows instantiation of arbitrary PHP classes with constructor arguments. Combined with the bundled symfony/process dependency, an attacker can instantiate Symfony\Component\Process\Process to execute arbitrary shell commands. This vulnerability bypasses the fix implemented for CVE-2025-57811 (patched in 5.8.7) [1][3].

Exploitation

Exploitation requires administrator-level permissions or access to the System Messages utility with allowAdminChanges enabled. The attack vector is through the admin panel: Settings → Entry Types → Title Format field. An attacker inserts a Twig payload such as {% set p = create("Symfony\\Component\\Process\\Process", [["id"]]) %}{{ p.mustRun.getOutput }}. When an entry of that type is created or edited, the payload executes, and the command output appears in the entry title [3].

Impact

Successful exploitation results in authenticated remote code execution as the web server user. In the default Docker setup, this is the root user, leading to full server compromise. The attacker can execute arbitrary commands, access sensitive data, and potentially pivot to other systems [3].

Mitigation

The vulnerability is fixed in Craft CMS 5.9.0-beta.1 and 4.17.0-beta.1. The fix restricts the create() function to only instantiate classes that extend yii\base\BaseObject, preventing the use of Symfony\Component\Process\Process and similar dangerous classes [2][3]. Users are strongly advised to update to the patched versions immediately.

AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
craftcms/cmsPackagist
>= 5.8.7, < 5.9.0-beta.15.9.0-beta.1
craftcms/cmsPackagist
>= 4.0.0-RC1, < 4.17.0-beta.14.17.0-beta.1

Affected products

2

Patches

1
e31e50849ad7

Restrict create() to BaseObject classes

https://github.com/craftcms/cmsbrandonkellyJan 5, 2026via ghsa
3 files changed · +24 3
  • CHANGELOG-WIP.md+1 0 modified
    @@ -8,6 +8,7 @@
     
     ### Development
     - Added support for referencing environment variables anywhere within settings that support them (e.g. `foo/$ENV_NAME/bar` or `foo-${ENV_NAME}-bar`). ([#17949](https://github.com/craftcms/cms/pull/17949))
    +- It’s no longer possible to instantiate objects that don’t extend `yii\base\BaseObject` via the `create()` Twig function. (GHSA-94rc-cqvm-m4pw)
     - Added the `uuid()` Twig function.
     - The `@parseRefs` GraphQL directive is now optional for each GraphQL schema. (GHSA-7x43-mpfg-r9wj)
     
    
  • src/Craft.php+1 2 modified
    @@ -46,8 +46,7 @@ class Craft extends Yii
         /**
          * @inheritdoc
          * @template T
    -     * @param class-string<T>|array|callable $type
    -     * @phpstan-param class-string<T>|array{class:class-string<T>}|callable():T $type
    +     * @param class-string<T>|array{class:class-string<T>}|array{__class:class-string<T>}|callable():T $type
          * @param array $params
          * @return T
          */
    
  • src/web/twig/Extension.php+22 1 modified
    @@ -79,6 +79,7 @@
     use Twig\TwigFilter;
     use Twig\TwigFunction;
     use Twig\TwigTest;
    +use yii\base\BaseObject;
     use yii\base\InvalidArgumentException;
     use yii\base\InvalidConfigException;
     use yii\db\Exception;
    @@ -1403,7 +1404,7 @@ public function getFunctions(): array
                 new TwigFunction('combine', 'array_combine'),
                 new TwigFunction('configure', [Craft::class, 'configure']),
                 new TwigFunction('cpUrl', [UrlHelper::class, 'cpUrl']),
    -            new TwigFunction('create', [Craft::class, 'createObject']),
    +            new TwigFunction('create', [$this, 'createFunction']),
                 new TwigFunction('dataUrl', [$this, 'dataUrlFunction']),
                 new TwigFunction('date', [$this, 'dateFunction'], ['needs_environment' => true]),
                 new TwigFunction('dump', [$this, 'dumpFunction'], ['is_safe' => ['html'], 'needs_context' => true, 'is_variadic' => true]),
    @@ -1482,6 +1483,26 @@ public function collectFunction(mixed $var): Collection
             return $collection;
         }
     
    +    /**
    +     * Creates a new object.
    +     *
    +     * @template T of BaseObject
    +     * @param class-string<T>|array{class:class-string<T>}|array{__class:class-string<T>} $type
    +     * @param array $params
    +     * @return T
    +     * @since 4.17.0
    +     */
    +    public function createFunction(string|array $type, array $params = []): BaseObject
    +    {
    +        $class = is_string($type) ? $type : ($type['__class'] ?? $type['class'] ?? null);
    +        if (!is_subclass_of($class, BaseObject::class)) {
    +            throw new InvalidArgumentException(sprintf('create() can only be used to create instances of %s.', BaseObject::class));
    +        }
    +
    +        /** @var BaseObject */
    +        return Craft::createObject($type, $params);
    +    }
    +
         /**
          * Generates a base64-encoded [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) for the given file path or asset.
          *
    

Vulnerability mechanics

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

References

4

News mentions

0

No linked articles in our index yet.