
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.
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.
| 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. |