Digitale Resilienz der Demokratie
Zivilgesellschaftliches Monitoring für sichere Infrastrukturen
WordPress-Sicherheitslücke bedroht 500 Millionen Websites
Vom WordPress-REST-Fehler bis zur Administratorübernahme
wp2shell gehört zu den bemerkenswertesten WordPress-Core-Angriffsketten der vergangenen Jahre. Nicht eine einzelne Schwachstelle, sondern das präzise Zusammenspiel zweier Fehler ermöglicht es, eine öffentlich erreichbare WordPress-Installation ohne vorherige Anmeldung von einer fehlerhaften REST-Verarbeitung über eine SQL-Injection bis zur Anlage eines neuen Administratorkontos zu führen.

Ich habe diese vollständige Kette gegen eine eigene, isolierte WordPress-6.9.1-Testinstanz rekonstruiert. Dabei ging es nicht darum, einen öffentlichen Proof of Concept lediglich auszuführen. Ziel war es, die einzelnen Zustandswechsel technisch zu verstehen, den Ablauf reproduzierbar zu machen und jeden wesentlichen HTTP-Schritt nachvollziehbar zu dokumentieren.
Das Ergebnis zeigt zugleich, was ich unter professionellem Vulnerability Engineering verstehe: Eine Schwachstelle erkennen, ihre tatsächliche Ausnutzbarkeit prüfen, den internen Mechanismus analysieren, falsche Annahmen ausschließen und daraus einen belastbaren technischen Nachweis erstellen.
Was ist wp2shell?
Der Name wp2shell bezeichnet die Angriffskette aus CVE-2026-63030 und CVE-2026-60137. CVE-2026-63030 betrifft eine Interpretation beziehungsweise Zuordnung von Requests und Handlern im WordPress-REST-Batch-Endpunkt. CVE-2026-60137 betrifft die unzureichende Normalisierung des Parameters author__not_in in WP_Query.
Die SQL-Injection allein ist nicht automatisch über jede WordPress-Installation erreichbar. Die Route-Confusion liefert jedoch den notwendigen Transportmechanismus: Ein Request wird unter den Bedingungen einer Route validiert, anschließend aber durch den Handler einer anderen Route verarbeitet. Dadurch kann ein Parameter einen Codepfad erreichen, dessen reguläre REST-Validierung ihn normalerweise abgefangen hätte.
Die vollständige wp2shell-Kette betrifft WordPress 6.9.0 bis 6.9.4 sowie WordPress 7.0.0 und 7.0.1. Die zugrunde liegende SQL-Injection CVE-2026-60137 betrifft zusätzlich WordPress 6.8.0 bis 6.8.5. WordPress 6.8.6 behebt die SQL-Injection; WordPress 6.9.5 und 7.0.2 beheben beide Bestandteile der Kette.
Die ursprüngliche Forschungsleistung muss dabei korrekt zugeordnet werden: WordPress nennt TF1T, dtro und haongo als Meldende der SQL-Injection. Die REST-Batch-Route-Confusion und die daraus entwickelte RCE-Kette wurden von Adam Kues bei Assetnote beziehungsweise Searchlight Cyber gemeldet. Meine Arbeit ist eine eigene vollständige Reproduktion und technische Auswertung dieser öffentlich bekannt gewordenen Kette, nicht die Behauptung ihrer Erstentdeckung.
Auch die Bewertung muss getrennt betrachtet werden. Für CVE-2026-60137 weist die NVD derzeit keine eigene NIST-Bewertung aus, sondern zeigt unter anderem die CNA-Bewertung von 5,9 und eine CISA-ADP-Bewertung von 9,1. Für CVE-2026-63030 wird eine CNA-Bewertung von 9,8 angezeigt. Der kritische Gesamteinfluss entsteht aus der erreichbaren Angriffskette, nicht aus einer undifferenzierten Gleichsetzung beider CVE-IDs.
Die erste Ursache: Desynchronisation im REST-Batch-Endpunkt
Der WordPress-REST-Batch-Endpunkt verarbeitet mehrere interne Requests innerhalb eines äußeren HTTP-Requests. In der verwundbaren Implementierung wurden dafür zwei parallele Arrays geführt: eines für die ermittelten Route-Handler und eines für die Validierungsergebnisse.
Konnte der Pfad eines Sub-Requests nicht verarbeitet werden, entstand ein WP_Error. Dieser Fehler wurde zwar im Validierungsablauf berücksichtigt, im parallelen Handler-Array fehlte jedoch ein entsprechender Eintrag. Beide Strukturen hatten danach nicht mehr dieselbe Länge und ihre Indizes beschrieben nicht länger denselben Request.
Die entscheidende Verschiebung lautet: Request N wird mit dem Handler des nachfolgenden Requests N+1 verarbeitet. Genau diese Richtung ist wichtig, um den Angriff technisch korrekt zu verstehen. Ein präparierter Request kann dadurch zunächst gegen eine unkritische oder unpassende Route geprüft werden und anschließend in einem leistungsfähigeren Handler landen.
Normale Batch-Requests dürfen sich nicht beliebig selbst verschachteln. WordPress kontrolliert dies über die Batch-Freigabe der jeweiligen Route und antwortet andernfalls mit rest_batch_not_allowed. Die Verschachtelung der Angriffskette ist deshalb kein reguläres Merkmal von /wp/v2/posts. Sie wird erst durch die fehlerhafte Handler-Zuordnung möglich.
Die zweite Ursache: author__not_in erreicht ungeprüft WP_Query
Für den SQL-Injection-Schritt wird ein Request verwendet, dessen sichtbarer Pfad wie eine einzelne Post-Abfrage behandelt wird. Die Collection-Parameter author_exclude, orderby und per_page gehören nicht zum Schema dieser Item-Route. Durch die Indexverschiebung gelangt derselbe Request anschließend jedoch in den Collection-Handler.
Dort wird der REST-Parameter author_exclude auf die interne WP_Query-Variable author__not_in abgebildet. In der verwundbaren Fassung wurde der Wert nicht zuverlässig auf eine Liste numerischer Benutzer-IDs reduziert, bevor er in die SQL-Bedingung für post_author NOT IN (…) übernommen wurde.
Damit ist die SQL-Injection erreicht. Die Einschränkung liegt nicht darin, dass mysqli_query() grundsätzlich nur SELECT-Abfragen ausführen könnte. Die Funktion kann unterschiedliche einzelne SQL-Statements verarbeiten. Sie führt jedoch keine durch Semikolon getrennten Mehrfachstatements wie mysqli_multi_query() aus. Der SELECT-Kontext ergibt sich hier aus der konkreten Position der manipulierten Eingabe innerhalb der von WP_Query erzeugten Abfrage.
Drei Nachweise für dieselbe SQL-Injection
Boolean-basierter Nachweis
Im ersten Schritt habe ich zwei logisch gegensätzliche Bedingungen eingesetzt. Die daraus resultierende Anzahl gefundener Beiträge wurde über die WordPress-Response sichtbar. Unterschiedliche Werte im Header X-WP-Total bildeten ein Boolean-Oracle und bestätigten, dass die eingeschleuste Bedingung die Datenbankabfrage beeinflusst.
Zeitbasierter Nachweis
Eine zweite, unabhängig messbare Bestätigung erfolgte über eine gezielte Zeitverzögerung. Dadurch ließ sich ausschließen, dass die Unterschiede der vorherigen Antworten lediglich durch Pagination, Cache-Zustände oder wechselnde Inhalte entstanden waren.
In-Band-UNION-Nachweis
Der dritte Nachweis nutzte eine UNION-Abfrage, deren Ergebnis sich als scheinbar reguläre WordPress-Post-Zeile in die Antwort einfügte. orderby=none verhinderte dabei eine für die UNION störende Sortierung. Ein vergrößertes per_page erhöhte das verfügbare Ergebnisfenster; es versetzte WP_Query jedoch nicht in einen besonderen „Full-Row-Modus“.
Diese drei Methoden belegen denselben Fehler aus unterschiedlichen Richtungen. Gerade diese Mehrfachbestätigung ist für eine belastbare Schwachstellenanalyse wichtig: Ein einzelner auffälliger Response beweist noch keine SQL-Injection. Erst reproduzierbare Kontrollbedingungen, Zeitmessungen und kontrollierte Rückgabewerte ergeben zusammen ein tragfähiges Bild.
Warum eine SELECT-basierte SQL-Injection trotzdem zum Administrator führt
Der ungewöhnlichste Teil von wp2shell beginnt dort, wo klassische SQL-Injection-Annahmen enden. Ohne Stacked Queries kann nicht einfach ein zusätzliches INSERT für einen Administrator an die Datenbankabfrage angehängt werden. Die Kette verwendet stattdessen reguläre WordPress-Funktionen als Schreibmechanismus.
Über UNION lassen sich vollständige Zeilen der Posts-Tabelle vortäuschen. WordPress verarbeitet die zurückgegebenen Datensätze als Post-Objekte und verwendet diesen Zustand innerhalb desselben Requests weiter. Technisch handelt es sich um eine In-Request-Post- beziehungsweise Object-Hydration. Dafür ist kein externer Redis- oder Memcached-Cache erforderlich.
Priming über oEmbed
Ein durch die UNION-Abfrage kontrollierter Post enthält Embed-Inhalte mit eigens gewählten URLs. WordPress verarbeitet diese Inhalte über den regulären oEmbed-Codepfad und legt dafür reale Cache-Posts in der Datenbank an. Die SQL-Injection bleibt SELECT-basiert; der erste persistente Schreibvorgang erfolgt durch WordPress selbst.
Rückgewinnung realer Objekt-IDs
Die neu angelegten oEmbed-Cache-IDs werden anschließend über denselben SQL-Injection-Kanal ausgelesen. Damit stehen reale, in der Datenbank vorhandene Post-IDs zur Verfügung, die in den folgenden Verarbeitungsschritten mit kontrollierten Objektinhalten verbunden werden können.
Changeset, Benutzerkontext und erneuter REST-Dispatch
Die nächste Stufe verbindet kontrollierte Post-Objekte mit einem Customizer-Changeset und einem darin referenzierten Benutzerkontext. Innerhalb der erzwungenen Verarbeitungsreihenfolge wird derselbe Request zur Benutzeranlage zweimal bewertet. Der erste Versuch wird ohne ausreichenden Benutzerkontext abgewiesen. Der zweite, inhaltlich identische Versuch wird während des manipulierten Zustands erneut verarbeitet und beantwortet die Benutzeranlage mit 201 Created.
Das Ergebnis ist kein übernommenes vorhandenes Konto, sondern ein neu angelegter WordPress-Benutzer mit Administratorrolle und selbst gewählten Zugangsdaten. Genau dieser Unterschied macht die Reproduktion besonders aussagekräftig: Benutzername und Passwort waren vorher nicht Bestandteil des Zielsystems und mussten weder ausgelesen noch erraten oder zurückgesetzt werden.
Der Administratorzugriff ist damit nachgewiesen. Ein anschließender Übergang zur PHP-Codeausführung hängt von der Konfiguration ab. Der WordPress-Dateieditor muss verfügbar und das betreffende Dateisystem beschreibbar sein. Ist beispielsweise DISALLOW_FILE_EDIT aktiviert, bleibt die Administratorübernahme bestehen, während dieser konkrete Weg zur Codeausführung blockiert sein kann.
Meine vollständige Reproduktion auf WordPress 6.9.1
Ich habe die Kette gegen WordPress Core 6.9.1 in einer eigenen isolierten Sandbox nachgebaut. Die Untersuchung umfasst zwei getrennte aufgezeichnete Ausführungen mit 42 beziehungsweise 30 HTTP-Requests. Die erste Sitzung diente der schrittweisen Bestätigung von Route-Confusion, Boolean-, Timing- und UNION-Verhalten. In der zweiten Ausführung wurde die vollständige Object-Hydration-Kette bis zum neu erzeugten Administrator reproduziert.
Die HAR-Dateien bilden den äußeren Netzwerkablauf ab: Request-Pfade, Header, Bodies, Antwortcodes, Antwortinhalte und zeitliche Abfolge. Besonders aussagekräftig ist der direkte Vergleich der beiden inhaltlich identischen Requests zur Benutzeranlage: zunächst Ablehnung, anschließend 201 Created.
Eine HAR-Datei allein beweist allerdings keine internen PHP-Objektzustände. Deshalb muss bei einer fachlich belastbaren Auswertung zwischen unmittelbar aufgezeichneten HTTP-Fakten und aus Quellcode, Debug-Protokollen oder Ablaufverhalten abgeleiteten internen Zuständen unterschieden werden. Diese Trennung ist ein wesentlicher Bestandteil meiner Analyse.
Die erfolgreiche Reproduktion wurde zusätzlich über den Login mit dem neu erzeugten Konto und die sichtbare Administratorrolle bestätigt. Sämtliche aktiven Tests erfolgten ausschließlich gegen die eigene lokale Testinstanz.
Was bei dieser Analyse über das Ausführen eines PoC hinausgeht
Ein öffentlicher Exploit kann zeigen, dass eine Schwachstelle unter bestimmten Bedingungen funktioniert. Für eine belastbare Sicherheitsbewertung reicht das nicht. Bei wp2shell mussten mehrere technische Ebenen gemeinsam verstanden werden: REST-Routing, Schema-Validierung, verschachtelter Batch-Dispatch, WP_Query und SQL-Kontext, Post-Hydration, oEmbed-Verarbeitung, Customizer-Changesets, Benutzerkontext und die erneute REST-Verarbeitung innerhalb eines laufenden Requests.
Meine Leistung bestand darin, diese Zustandsmaschine in einer kontrollierten Umgebung vollständig nachzuvollziehen. Dazu gehörten die Analyse des WordPress-Quellcodes, die Rekonstruktion dynamischer Objekt-IDs, die Entwicklung kontrollierter Vergleichsbedingungen, die Erfassung der HTTP-Kommunikation und die verständliche Übersetzung des Ablaufs in eine prüfbare Dokumentation.
Genau hier greifen meine beiden Systeme ineinander. KRITIS 3.0 erfasst öffentlich sichtbare Komponenten und priorisiert mögliche Schwachstellen passiv. vtdr-v1.0 übernimmt ausgewählte Kandidaten und führt die aktive Verifikation ausschließlich in einer isolierten, versionsgebundenen Testumgebung durch. HTTP-Aufzeichnungen, technische Protokolle, festgelegte Prüfregeln und wiederholbare Teststände machen aus einem Scanner-Hinweis einen nachvollziehbaren Sicherheitsbefund.
Der dokumentierte Projektbestand umfasst zum Stand vom 24. Juni 2026 insgesamt 1.230 Exploit-Nachweise. Darunter befinden sich 971 Rekonstruktionen veröffentlichter CVE-Schwachstellen sowie 202 eigenständig als 0-Day dokumentierte Funde. Da einzelne ursprüngliche 0-Day-Funde später eine CVE-Kennung erhalten haben, sind diese Kategorien nicht ohne Weiteres zu addieren. Eine automatisierte Prüfinstanz bewertet Nachweise anhand von 48 Regeln; wiederholte Regressionstests prüfen, ob sie unter festgelegten Bedingungen erneut bestätigt werden können.
Wie WordPress die Angriffskette geschlossen hat
Die Korrektur besteht aus mehreren Schutzebenen. Beim fehlerhaften Batch-Pfad wird der WP_Error nun auch in das Handler-Array geschrieben, sodass Request-, Validierungs- und Handler-Indizes synchron bleiben. WP_Query normalisiert author__not_in mit wp_parse_id_list() zu gültigen numerischen IDs. Zusätzlich verhindern Reentrancy-Prüfungen in serve_request() und rest_api_loaded(), dass während eines bereits laufenden Dispatchs ein neuer Top-Level-REST-Zyklus gestartet wird.
Diese Mehrschichtigkeit ist eine wichtige Lehre aus wp2shell. Nicht nur der unmittelbar sichtbare Injection-Punkt wurde geschlossen. WordPress beseitigte auch den Transportmechanismus und erschwerte die für die Eskalation notwendige erneute REST-Verarbeitung.
Die Sicherheitsupdates 6.8.6, 6.9.5 und 7.0.2 wurden am 17. Juli 2026 veröffentlicht. WordPress 6.8.6 enthält nur die für diesen Zweig relevante SQL-Injection-Korrektur, weil die Route-Confusion WordPress 6.8 nicht betrifft. Beide CVEs wurden am 21. Juli 2026 in den CISA-Katalog bekanntermaßen ausgenutzter Schwachstellen aufgenommen. Betreiber sollten nicht lediglich auf die genannten Mindestversionen, sondern auf eine aktuelle unterstützte WordPress-Version aktualisieren.
Relevanz für Vulnerability Management und ISO/IEC 27001
wp2shell zeigt, warum ein professionelles Vulnerability Management nicht bei einer Versionsliste enden darf. Eine Schwachstelle mit mittlerer Einzelbewertung kann durch einen zweiten Fehler plötzlich Teil einer kritischen Angriffskette werden. Für die tatsächliche Priorisierung müssen deshalb technische Erreichbarkeit, Kombinierbarkeit, vorhandene Schutzmaßnahmen und der mögliche Geschäftsschaden gemeinsam bewertet werden.
Für ein Informationssicherheitsmanagementsystem liefert eine solche Analyse verwertbare Evidenz: betroffene Assets und Versionen, technische Ursache, Angriffsvoraussetzungen, beobachtete Wirkung, empfohlene Behandlung und Nachweis der Patch-Wirksamkeit. Das unterstützt einen risikobasierten Prozess nach ISO/IEC 27001 und Vorgehensweisen des BSI IT-Grundschutzes, ersetzt aber weder die organisatorische Risikoverantwortung noch eine Zertifizierung.
Verantwortungsvolle Sicherheitsforschung
Alle beschriebenen aktiven Versuche wurden ausschließlich gegen eigene, isolierte Testsysteme durchgeführt. Eine Anwendung gegen fremde Systeme ohne ausdrückliche Berechtigung kann strafbar sein. Die dargestellten technischen Details dienen der fachlichen Einordnung, der reproduzierbaren Forschung und der Verbesserung von Schutzmaßnahmen.
Die wp2shell-Kette verbindet CVE-2026-63030, eine Desynchronisation zwischen Validierung und Handler-Zuordnung, mit CVE-2026-60137, der fehlenden Normalisierung von author__not_in. Erst das Zusammenspiel beider Schwachstellen ermöglicht die unauthentifizierte SQL-Injection und die anschließende Administratorübernahme.
Der vollständige Nachbau zeigt meine Arbeitsweise als IT-Security Engineer deutlicher als eine reine Aufzählung von Werkzeugen oder Zertifikaten: komplexe Angriffspfade verstehen, Quellcode und Laufzeitverhalten zusammenführen, Hypothesen durch mehrere Verfahren bestätigen, Grenzen des Nachweises offen benennen und Ergebnisse so dokumentieren, dass andere Fachleute sie prüfen können.
Genau darin liegt für mich der Unterschied zwischen einem automatisierten Scan und echter Schwachstellenforschung: Der Scanner findet einen Hinweis. Professionelles Vulnerability Engineering macht daraus eine nachvollziehbare technische Entscheidung.
Quellen und technische Referenzen
- VTDR-Rekonstruktion: Vollständiger Angriffsweg
- CVE-2026-60137: NVD-Eintrag zur SQL-Injection
- CVE-2026-63030: NVD-Eintrag zur REST-Batch-Route-Confusion
- Technische Originalanalyse: Searchlight Cyber / Assetnote – wp2shell
- WordPress-Code zum Batch-Endpunkt: WP_REST_Server::serve_batch_request_v1()
- WordPress-Code zur SQL-Korrektur: WP_Query::get_posts()
- ISO/IEC 27001:2022: Information Security Management Systems
- BSI-Schwachstellenmanagement: Sicherheitsmaßnahmen und Schwachstellenmanagement