
CVE-2026-63030
Proof-of-concept für eine nicht authentifizierte SQL-Injection im WordPress-Core, die über eine REST-Batch-Routen-Verwechslung (CVE-2026-63030) zu Remote-Code-Ausführung führt.
Der Exploit benötigt keine Anmeldedaten und keine Konfiguration, abgesehen von einem erreichbaren Ziel. Er erreicht die Injektion über einen einzigen Endpunkt: POST /wp-json/batch/v1.
Der Befund selbst ist eine nicht authentifizierte SQL-Injection – check bestätigt sie und read demonstriert beliebiges Datenbank-Lesen. Der Befehl shell ist ein optionaler Post-Exploitation-Schritt (wiederhergestellte Admin-Zugangsdaten → Plugin-Upload → Befehlsausführung), der enthalten ist, um die volle Auswirkung zu demonstrieren; er ist nicht die Schwachstelle.
| Branch | Betroffen | Behoben in |
|---|
| 6.9.x | 6.9.0 – 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | 7.0.2 |
Versionen vor 6.9.0 sind von dieser Kette nicht betroffen.
Der REST-Batch-Endpunkt (/batch/v1) erfordert keine Authentifizierung und führt mehrere Unteranfragen in einem einzigen Aufruf aus. Dabei verlässt er sich darauf, dass jede Unteranfrage einzeln validiert und auf Berechtigungen geprüft wird.
serve_batch_request_v1() baut zwei parallele Arrays auf – $matches (den passenden Handler pro Unteranfrage) und $validation (das Validierungsergebnis pro Unteranfrage) – und indexiert beide beim Versand über denselben Offset. Eine Unteranfrage, deren Pfad bei wp_parse_url() fehlschlägt, wird an $validation angehängt, nicht aber an $matches. Dadurch geraten die Arrays aus dem Takt, und eine Unteranfrage wird unter dem Handler einer anderen Unteranfrage ausgeführt. Das ist die Routen-Verwechslung.
Der PoC verschachtelt diesen Grundbaustein zweimal:
POST /wp/v2/posts-Anfrage mit einem requests-Body wird unter dem Batch-Handler selbst ausgeführt. Da sie als Posts-Anfrage validiert wurde, wird ihre requests-Liste nie gegen das Batch-Schema geprüft, sodass ihre Unteranfragen GET verwenden dürfen – die Methoden-Allowlist wird umgangen.GET /wp/v2/users-Anfrage mit einem author_exclude-String (das Users-Schema kennt keinen solchen Parameter, daher passiert der Wert die Validierung unverändert) unter posts get_items() ausgeführt. Dort wird author_exclude auf die WP_Query-Query-Variable author__not_in abgebildet, die die verwundbare Version als String in SQL interpoliert.Das Ergebnis ist eine boolesche und zeitbasierte Blind-SQL-Injection, die ohne Authentifizierung erreichbar ist. Mit Datenbank-Lesezugriff kann der Passwort-Hash des Administrators wiederhergestellt werden; ist er geknackt, ermöglicht Admin-Zugriff die Code-Ausführung über einen Plugin-Upload.
Python 3.8+ und die Standardbibliothek. Keine Drittanbieter-Abhängigkeiten.
Führen Sie es aus dem Repository-Verzeichnis aus:
./wp2shell.py <command> <url> [options]
Oder pip install ., um den Befehl wp2shell in Ihren PATH aufzunehmen.
Bestätigt die Ausnutzbarkeit anhand einer differentiellen Zeitverzögerung. Es liest keine Daten und ändert nichts.
./wp2shell.py check http://target
./wp2shell.py read http://target # server fingerprint
./wp2shell.py read http://target --preset users # user logins and password hashes
./wp2shell.py read http://target --query "SELECT @@version"
Optionale Post-Exploitation. Erfordert gültige Administrator-Zugangsdaten; die Injektion stellt den Passwort-Hash wieder her. Geben Sie hier den wiederhergestellten Klartext an.
./wp2shell.py shell http://target --user admin --password '<recovered>' --cmd id
./wp2shell.py shell http://target --user admin --password '<recovered>' -i # interactive shell
shell lädt eine Plugin-Webshell hoch (geschützt durch einen zufälligen Pfad und ein pro Lauf generiertes Token) und gibt deren Pfad aus. Entfernen Sie sie, wenn Sie fertig sind.
| Option | Gilt für | Beschreibung |
|---|---|---|
--rest-route | alle | /?rest_route=/batch/v1 verwenden (für Websites ohne Pretty Permalinks). |
--proxy URL | alle | Datenverkehr über einen HTTP-Proxy leiten (z. B. Burp). |
--timeout N | alle | Anfrage-Timeout in Sekunden. |
--sleep N | check | Verzögerung zur Bestätigung der Injektion. |
--preset | read | fingerprint oder users. |
--query | read | Ein skalarer SQL-Ausdruck, der gelesen werden soll. |
--prefix | read | Tabellenpräfix der Datenbank (Standard: wp_). |
--max-length N | read | Maximal gelesene Zeichen pro Wert (Standard: 128). |
--user / --password | shell | Admin-Zugangsdaten (Klartext, aus dem Hash wiederhergestellt). |
--cmd | shell | Auszuführender Befehl (bei Verwendung von -i weglassen). |
-i / --interactive | shell | Öffnet nach dem Bereitstellen eine interaktive Shell. |
Aktualisieren Sie auf WordPress 6.9.5 oder 7.0.2. Blockieren Sie bis dahin sowohl /wp-json/batch/v1 als auch den Abfrageparameter rest_route=/batch/v1 am Edge, oder verlangen Sie Authentifizierung für den Batch-Endpunkt über den Filter rest_pre_dispatch.
Nur für autorisierte Sicherheitstests. Verwenden Sie es ausschließlich gegen Systeme, die Ihnen gehören oder für deren Test Sie eine ausdrückliche schriftliche Genehmigung haben. Der Autor übernimmt keine Haftung für Missbrauch.