
Nur-Lese-Triage-Skript für WordPress-Websites nach PHP2Shell (CVE-2026-109618, CVE-2026-109619). Prüft auf Anzeichen einer Kompromittierung und schreibt einen Bericht pro Website.
Ein schreibgeschütztes Triage-Skript für WordPress-Seiten nach PHP2Shell (CVE-2026-109618 und CVE-2026-109619), dem Paar kritischer Remote-Code- Execution-Schwachstellen in der PHP-Engine, die am 21. Oktober 2026 offengelegt wurden.
Das Patchen von PHP schließt die Tür, aber Amazons Honeypots verzeichneten einen Ausnutzungsversuch am 17. Oktober, vier Tage vor der öffentlichen Offenlegung, als die Schwachstellen noch unter Embargo standen. Das ist die früheste Aktivität, die jemand veröffentlicht hat, nicht der Tag, an dem die Angriffe begannen, also ist es als Untergrenze zu behandeln. Jede Seite, die während dieses Zeitraums mit einem ungepatchten PHP erreichbar war, hat möglicherweise bereits eine Backdoor, und ein PHP-Update entfernt diese nicht. Dieses Skript prüft eine oder viele WordPress-Seiten auf die Dinge, die ein Angreifer mit Codeausführung typischerweise hinterlässt, und schreibt für jede einen Markdown-Bericht.
Eine dritte Schwachstelle, CVE-2026-115560, wurde am 23. Oktober
angekündigt. Es gibt noch keinen gepatchten Build: Die Punkt-Releases vom
Oktober (8.2.35, 8.3.36, 8.4.27, 8.5.12) decken sie nicht ab, und der Fix
soll mit dem nächsten regulären Release im November erscheinen. php.net bittet
alle, bis dahin session.upload_progress.enabled=0 in php.ini oder im
PHP-FPM-Pool zu setzen. Das Skript prüft, ob dies erfolgt ist.
Es ändert oder löscht niemals etwas. Es listet auf, verifiziert und berichtet. Was mit den Ergebnissen geschieht, liegt bei Ihnen.
| Bereich | Prüfung |
|---|---|
| PHP | Installierte CLI-Version vs. die gepatchten Releases (8.2.35, 8.3.36, 8.4.27, 8.5.12). Markiert End-of-Life-Zweige, die von php.net kein Release erhielten; siehe Ältere PHP-Zweige. |
| Upload-Fortschritt | Ob session.upload_progress.enabled=0, die Mitigation für CVE-2026-115560, dort gesetzt ist, wo es zählt: in der Sicht von PHP-FPM selbst (php-fpm -i), in jeder lesbaren php.ini, conf.d, im FPM-Pool und in der Apache-Konfiguration. .user.ini und .htaccess auf Seitenebene werden gemeldet, aber nicht angerechnet, da php.net angibt, dass sie für diese Einstellung nicht funktionieren. |
| Kern-Integrität | wp core verify-checksums gegen wordpress.org |
| Plugin-Integrität | wp plugin verify-checksums --all, und listet Premium-/benutzerdefinierte Plugins auf, die nicht verifiziert werden können |
| Uploads | Jede PHP-ausführbare Datei unter wp-content/uploads (ignoriert „Silence is golden"-Stubs) |
| Aktuelle Änderungen | PHP- und Konfigurationsdateien, die seit dem Beginn des Zeitraums geändert wurden (Standard 2026-10-17). WordPress-Übersetzungspakete (wp-content/languages/**/*.l10n.php) werden separat gezählt, sobald sie einen strengen Formtest bestehen; eines, das ihn nicht besteht, wird markiert. |
| Prepend-Hooks | auto_prepend_file / auto_append_file in .user.ini, .htaccess, php.ini |
| Must-use-Plugins | Inhalte von wp-content/mu-plugins (wird bei jeder Anfrage ausgeführt, in der normalen Plugin-Liste verborgen) |
| Code-Muster | Klassische Web-Shell-Signaturen (eval(base64_decode(...)), $_POST[...](), etc.) in wp-content |
| Benutzer | Alle Administratoren und jedes Konto, das innerhalb des Zeitraums registriert wurde |
| Datenbank | siteurl/home, Optionen und Beiträge, die injizierte Skripte oder obfuskierten Code enthalten |
| Cron | Geplante WP-Cron-Ereignisse |
| Logs | Zugriffslogs: 5xx-Antworten pro Tag für den Zeitraum vs. eine Baseline von bis zu 7 Tagen davor, plus die URLs, die im Zeitraum 5xx zurückgeben. Fehler- und PHP-FPM-Logs: Worker-Abstürze (Segfault/Bus Error). |
| Server | Die Crontab des Seiten-Benutzers und SSH authorized_keys |
| Docker | Jeder laufende Container mit einer php-Binärdatei: die PHP-Version darin und ob der Container noch einen älteren Build als das Image auf der Festplatte ausführt (gezogen, aber nicht neu erstellt) |
WP-CLI wird mit --skip-plugins --skip-themes ausgeführt, damit ein mit einer Backdoor versehenes Plugin oder Theme während der Prüfung nicht ausgeführt werden kann.
Auf jedem zu prüfenden Rechner:
bash 3.2+, find, grep und sort -V (Standard auf Linux-Hosts; funktioniert auch von einer Standard-macOS-Workstation aus)php im Pfad, für die VersionsprüfungFür entfernte Seiten: SSH-Schlüsselzugriff von dem Rechner, auf dem Sie das Skript ausführen. Das Skript führt sich selbst über SSH aus; auf dem entfernten Host muss außer dem oben Genannten nichts installiert werden.
curl -O https://raw.githubusercontent.com/brentmartinmiller/php2shell-check/main/php2shell-check.sh
chmod +x php2shell-check.sh
# One site
./php2shell-check.sh /var/www/example.com
less php2shell-reports/*.md
# Every site on the box
printf '%s\n' /var/www/*/public_html > sites.txt
./php2shell-check.sh sites.txt
cat php2shell-reports/SUMMARY.md
Führe es nach Möglichkeit als der Benutzer aus, dem die Website-Dateien gehören. Es funktioniert als root
(WP-CLI erhält --allow-root), aber die crontab- und SSH-Key-Prüfungen untersuchen dann
die von root statt die des Web-Benutzers.
git clone https://github.com/brentmartinmiller/php2shell-check.git
cd php2shell-check
cp sites.txt.example sites.txt # edit: one site per line
./php2shell-check.sh sites.txt
Für jeden user@host:path-Eintrag meldet sich das Skript per SSH an, führt sich dort selbst aus und bringt den Bericht zurück nach php2shell-reports/ auf deinem Rechner. Auf dem Remote-Host wird nichts installiert. SSH läuft mit BatchMode=yes, daher ist schlüsselbasierter Zugriff erforderlich, der ohne Passwortabfrage funktioniert.
Die meisten Shared Hosts (Hostinger, SiteGround, Bluehost und ähnliche) bieten SSH an, haben aber oft kein wp im Pfad. Prüfe dies mit which wp. Falls es fehlt, lade WP-CLI einmalig in dein Home-Verzeichnis herunter und verweise das Skript darauf:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
WP="php $HOME/wp-cli.phar" ./php2shell-check.sh domains/example.com/public_html
Ohne WP-CLI laufen die Dateisystem-Prüfungen trotzdem und der Bericht vermerkt, was übersprungen wurde. Frage in jedem Fall beim Host direkt nach, wann das gepatchte PHP ausgerollt wurde, denn das ist das Einzige, was eine Prüfung auf Kontoebene nicht sehen kann.
sites.txt enthält eine Site pro Zeile, entweder einen lokalen Pfad oder user@host:path.
Relative Remote-Pfade sind relativ zum Home-Verzeichnis des SSH-Benutzers. Zeilen,
die mit # beginnen, werden ignoriert. Siehe sites.txt.example. Die Datei steht in
.gitignore, damit Client-Hostnamen nicht in einem Commit landen.
Nicht-standardmäßige SSH-Ports, Schlüssel und Jump-Hosts gehören in ~/.ssh/config:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
Dann listen Sie die Site als client-a:/var/www/html auf.
php -v auf einem Docker-Host sagt Ihnen nichts über die Container, und das
Herunterladen eines festen Images behebt nichts, bis der Container daraus neu
erstellt wird. Die Site eines Teams blieb aus genau diesem Grund drei Tage lang
auf 8.3.35, nachdem das Image heruntergeladen worden war.