TL;DR: Mein erster Durchgang nannte die Seite gehärtet und bewertete sie mit HOCH. Der zweite Durchgang fand eine unauthentifizierte WordPress-Core-RCE, die bereits im CISA-KEV-Katalog stand, eine CORS-Reflexion mit Credentials, die ich an der falschen URL getestet hatte, und drei Plugins, die nie im HTML auftauchten. Gleiche Oberfläche, andere Fragen, anderes Urteil: KRITISCH.
Die Ausgangslage
Ich hatte diese Seite schon einmal auditiert. Das Fazit war beruhigend: Login und Admin hinter Basic Auth, die REST-User-Endpoints geschlossen, xmlrpc zu, Kommentare deaktiviert. Für eine WordPress-Installation ist das überdurchschnittlich, und der Bericht sagte das auch.
Dann bat mich der Auftraggeber, so tief wie möglich zu graben. Kein neuer Zugang, keine neue Technik. Ich startete auf derselben öffentlichen Oberfläche und stellte andere Fragen. Das Ergebnis war keine Ergänzung des ersten Berichts. Es war eine Korrektur.
Ich habe alle identifizierenden Informationen entfernt. Das Ziel wird nur nach Branche und Technologieklasse beschrieben, damit das Muster wiederverwendbar bleibt.
Das Muster
Ein Rescan ist nicht "den ersten Scan nochmal laufen lassen". Es ist eine andere Checkliste. Drei blinde Flecken machten aus demselben Ziel ein HOCH — und dann ein KRITISCH.
Blinder Fleck 1: Die Version galt als "aktuell genug"
Der erste Durchgang notierte die Core-Version und ging weiter, weil damals nichts dazu passte. Der zweite Durchgang map-te die exakte Version gegen die seither veröffentlichten Advisories — und fand einen Path Traversal in der Page-Template-Auflösung mit bedingter RCE. Der Eintrag stand im CISA-KEV-Katalog und wurde Stunden nach der Veröffentlichung massenhaft ausgenutzt. Die Ziel-Version lag im betroffenen Bereich, und das Theme-Verzeichnis erfüllt die dokumentierte erste Vorbedingung.
Die Lehre ist nicht "Version prüfen". Sondern: die Version gegen den Advisory-Stand zum Audit-Zeitpunkt mappen — und verstehen, dass eine Härtungs-Checkliste eine Core-Lücke nicht abdeckt.
Blinder Fleck 2: CORS wurde an der falschen URL getestet
Mein erster Durchgang testete Cross-Origin-Header auf der Startseite und fand nichts. Das ist bei WordPress ein naheliegender, aber falscher Test. Die REST-API unter /wp-json/ ist der entscheidende Endpoint, und die Fallback-Form ?rest_route= muss separat geprüft werden. Beide reflektierten einen beliebigen Origin zusammen mit Access-Control-Allow-Credentials: true — eine Kombination, die jeder Website, die ein eingeloggter Nutzer besucht, credentialisierte Cross-Origin-Aufrufe erlaubt. Der WordPress-Nonce reduziert den praktischen Impact, aber die Fehlkonfiguration ist real — und von der Startseite aus unsichtbar.
Blinder Fleck 3: Das Inventar kam aus dem HTML
Das Fingerprinting im HTML ergab sieben Plugins — das, was die Seite preisgab. Der zweite Durchgang verglich existierende Plugin-Verzeichnisse mit einer Nicht-Existenz-Baseline: ein fehlendes Plugin liefert die 404-Seite der Website, ein existierendes eine Verzeichnis-Antwort. Dieses Differential förderte drei weitere Plugins zutage: ein Sharing-Plugin mit offenem Stored-XSS-Advisory, ein Custom-Fields-Plugin mit zwei offenen Advisories und ein Premium-Caching-Plugin, dessen Version extern nicht bestimmbar war. Genauso wichtig war das Fehlende: kein Backup-Plugin, kein Security-Plugin, kein Aktivitätsprotokoll. Eine Kompromittierung hinterließe hier keine wiederherstellbare Kopie und keine Spur.
Befunde
| Schwere | Befund | Nachweis |
|---|---|---|
| KRITISCH | Unauthentifizierter Core-Path-Traversal, bedingte RCE, in CISA KEV | Version im betroffenen Bereich; Theme-Vorbedingung aus der öffentlichen Body-Class beweisbar |
| KRITISCH | CORS-Reflexion mit Credentials auf der REST-API | Jeder Origin reflektiert mit Access-Control-Allow-Credentials: true auf /wp-json/ und ?rest_route= |
| HOCH | Drei Schwester-Domains ohne DMARC und mit Softfail-SPF | Mandanten-Phishing ganz ohne Ausnutzung möglich |
| HOCH | Theme liefert jQuery von 2012 und überschreibt die aktuelle Core-Version | Bekannte XSS- und Prototype-Pollution-Advisories; Konsolenwarnung in Produktion |
| MITTEL | Drei versteckte Plugins mit offenen CVEs | Sharing-, Custom-Fields- und Caching-Plugin nur per Verzeichnis-Differential gefunden |
| MITTEL | Kein Backup-, Security- oder Audit-Plugin installiert | Bei Ausnutzung der RCE weder Wiederherstellung noch Detektion |
| MITTEL | Undeklariertes Calltracking über den Tag Manager geladen | Ein Cookie, das die Datenschutzerklärung nicht nennt |
| MITTEL | Kein HSTS, keine CORS-Allow-List, keine Security-Header auf der Hauptfläche | Basic-Auth-Credentials in feindlichen Netzen downgradebar |
| NIEDRIG | FTP offen, Banner nennt den Betreiber | Infrastruktur-Admin offengelegt; Brute-Force-Fläche |
Der Angriffspfad
Reproduktion im Labor
Jeder Befehl unten zielt auf einen Lab-Container, den ich selbst kontrolliere. Nichts davon ist auf ein laufendes System gerichtet.
# 1. Version und Theme-Vorbedingung (Lab-WordPress 6.8.3 mit page-Template-Ordner)
curl -s http://lab.local/ | grep -E 'generator|body class'
# <- generator 6.8.3, Body-Class enthält page-template-page-templates
# 2. CORS-Differential: Startseite vs. API-Wurzel vs. Fallback
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' http://lab.local/
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' http://lab.local/wp-json/
curl -s -D - -o /dev/null -H 'Origin: https://evil.example' 'http://lab.local/?rest_route=/'
# <- die API-Antworten tragen Access-Control-Allow-Origin: https://evil.example
# 3. Versteckte Plugins per Differential
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' http://lab.local/wp-content/plugins/gibt-es-nicht/
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' http://lab.local/wp-content/plugins/sharing-plugin/
# <- 404 mit der Site-Seite vs. 403 mit leerem Apache-Body
Erkennung im eigenen Bestand
- Jede Core- und Plugin-Version gegen den Advisory-Stand des Audit-Datums mappen, nicht gegen "wir haben kürzlich aktualisiert". Ein CISA-KEV-Eintrag ändert die Priorität sofort.
- CORS auf
/wp-json/und?rest_route=prüfen, nie nur auf der Startseite. - Das Verzeichnis-Differential auf den eigenen Plugin-Ordner anwenden und mit dem geglaubten Inventar vergleichen.
- Die Fehlliste explizit prüfen: Backup-, Security- und Audit-Log-Plugins. Fehlende Resilienz ist ein Befund.
- Jedes externe Skript gegen eine Allow-List prüfen und Subresource Integrity verlangen, wo der Anbieter es unterstützt; die Supply-Chain-Kampagnen 2026 kamen über Update-Server und CDNs, nicht über Code.
Behebung
# innerhalb von 24 Stunden
1. Core auf den gepatchten Release des betroffenen Zweigs aktualisieren
2. CORS: Reflexion durch feste Allow-List ersetzen, niemals mit Credentials
3. DMARC für jede Domain des Portfolios, nicht nur für die Hauptdomain
4. Datenbank-Konsole gaten (VPN oder IP-Allowlist, Auth, Rate-Limit)
# innerhalb einer Woche
5. jedes Plugin auf den aktuellen Release heben; Premium-Plugins manuell prüfen
6. Theme-jQuery durch Core-jQuery ersetzen und die Theme-Skripte fixen
7. Header-Baseline und HSTS auf jedem Vhost ausliefern
8. Backup-, Security-Monitoring- und Activity-Log-Plugins installieren
# innerhalb eines Monats
9. Credentials und Salts nach dem Core-Update rotieren
10. beide externen Agenturen per AVV und MFA binden, Patch-SLA festschreiben
Sobald ein Angreifer Zugriff hat, kann er folgende Aktionen durchführen:
Fazit — was ich daraus mitnehme
- Ein gehärteter Perimeter ist kein sicherer Kern. Basic Auth schützt den Login, nicht die Version.
- Den Endpoint testen, der zählt. Bei WordPress liegt CORS an der API-Wurzel, nicht auf der Startseite.
- Ein Inventar aus dem HTML ist eine Marketing-Liste, keine Asset-Liste. Ein Differential nutzen.
- Nach dem Fehlenden fragen. Kein Backup und kein Log auf einer Seite mit Mandantendaten ist selbst ein Risiko.
- Ein Rescan mit neuen Fragen ist mehr wert als eine Wiederholung der alten.