Katze im dunklen Kapuzenpulli sitzt nachts mit Kaffeetasse am Schreibtisch vor einem Server-Regal mit fünf abgeschalteten Geräten und einem großen roten Notaus-Knopf.

PHP-Lücken im Selbsthosting: Fünf Container und ein Stopp-Knopf

Was passiert ist

Am 21. Oktober 2026 hat das PHP-Projekt eine Meldung veröffentlicht, bei der man den Kaffee besser vorher abstellt: zwei kritische Schwachstellen (CVE-2026-109618 und CVE-2026-109619), die Remote Code Execution ohne Anmeldung ermöglichen können. Die Fixes stecken in PHP 8.5.12, 8.4.27, 8.3.36 und 8.2.35. Betroffen ist laut Meldung alles, was Webanfragen verarbeitet, also PHP-FPM, Apache mit mod_php, php-cgi, FrankenPHP und LiteSpeed. Bei mod_php nennt php.net das Risiko am höchsten, dafür gibt es einen gezeigten Exploit.

Das BSI hat schon vor der Veröffentlichung der Patches gewarnt, ungewöhnlich deutlich: Sein Vizepräsident Thomas Caspers sprach von „unverzüglichem Handlungsbedarf“. Das Amt hält eine großfächige Ausnutzung innerhalb weniger Stunden nach den Updates für wahrscheinlich, unter anderem weil Angreifer mit KI-gestützten Werkzeugen schnell Exploits bauen können. Die zugehörige Cybersicherheitswarnung nennt einen CVSS-4.0-Wert von 9,2, für die Ausnutzung sei weder Anmeldung noch Nutzerinteraktion nötig. Bestätigte Angriffe waren dort zum Stand der Warnung nicht bekannt, wohl aber unbestätigte Berichte über existierende Exploits. Die KI, die mir sonst beim Schreiben hilft, hilft offenbar auch der Gegenseite.

Das ist die Sorte Nachricht, bei der man als Selbsthoster kurz nachsieht, was eigentlich alles PHP spricht. Bei mir: WordPress, Nextcloud, Heimdall und webtrees, zusammen fünf Container, weil Nextcloud sich mit App und Cron gleich doppelt einträgt. WordPress und Nextcloud laufen bei mir mit Apache und mod_php, also genau in dem Fall, den php.net als besonders gefährdet nannte. Man freut sich ja über Aufmerksamkeit, aber so?

Erst mal alles anhalten, dann nachdenken

Meine erste Reaktion war unspektakulär: Die fünf betroffenen Container gestoppt und Zugänge geschlossen. Ein Patch lag zu dem Zeitpunkt in meinen Images noch nicht vor, und ein Dienst, der gerade nicht erreichbar ist, wird selten gehäckt. Mein Blog war damit kurz offline, mein Nextcloud auch. Die Welt hat es verkraftet.

Weil ich dem Zufall nicht traue, habe ich zusätzlich die Zeit seit der Meldung untersucht: Logs, docker diff im Container und eine Dateisuche nach neuen oder veränderten Dateien. Gefunden habe ich nichts Auffälliges, nur das übliche Rauschen, vor allem Bot-Anfragen auf xmlrpc.php.

Ich schreibe das bewusst vorsichtig: Ich habe keine Hinweise auf eine Kompromittierung gefunden. Ausschließen kann ich sie mit diesen Mitteln nicht, nur begründen, warum ich sie für unwahrscheinlich halte.

Image-Tag ist kein Patchbeweis

Am 22. Oktober waren neue Images da, zumindest behaupteten das die Tags. Ich habe es nachgemessen, denn ein Docker-Hub-Zeitstempel verrät mir, wann ein Image gebaut wurde, und nicht, welche PHP-Version darin steckt. Der Test ist banal:docker run --rm --entrypoint php <image> -v

Das Ergebnis: WordPress lieferte 8.3.36, Nextcloud und Heimdall 8.5.12, webtrees 8.4.27. Alle vier auf dem Stand der Meldung.

Ein Detail, das ich ehrlich dazuschreiben will: Die Dockerfile-Datei auf GitHub zeigte zeitweise noch 8.3.35, obwohl der Commit mit dem Fix schon da war. Wer sich nur auf die Quelle verlässt, hätte sich entspannt zurückgelehnt. Ich habe stattdessen im Image selbst gemessen. Seitdem vertraue ich keinem Tag mehr, den ich nicht selbst mit php -v abgefragt habe.

Stack für Stack wieder hoch, mit Messung dazwischen

Gestartet habe ich nicht alles auf einmal, sondern Stack für Stack. Nach jedem Neustart: Container-Status, ein Blick in die Logs und php -v im laufenden Container, damit nicht nur das Image, sondern auch das, was wirklich läuft, stimmt. Erst wenn ein Dienst sauber antwortete, kam der nächste dran. Das dauert länger als docker compose up -d für alles, aber wenn etwas klemmt, weiß ich wenigstens, wo.

Was ich nebenbei verbessert habe

So ein Vorfall ist ein guter Anlass, die Dinge anzufassen, die man seit Monaten „demnächst“ aufräumen wollte.

Restart-Policy. always startet auch Container, die man bewusst gestoppt hat. Für den Notfall ist unless-stopped die bessere Wahl: Was ich anhalte, bleibt angehalten.

Admin-Oberflächen nicht ins Netz. Docker umgeht bei veröffentlichten Ports die Firewall (ufw), das ist keine neue Erkenntnis, aber eine, die man gern vergisst. Nginx Proxy Manager und Portainer hängen deshalb jetzt nur noch auf 127.0.0.1 und sind per SSH-Tunnel erreichbar. Bequem ist das nicht, soll es aber auch nicht sein.

Login-Schutz hinter dem Proxy. Steht WordPress hinter einem Reverse Proxy, sieht ein Login-Schutz-Plugin zunächst nur die IP des Proxys. Damit es die echte Besucher-IP sperren kann, muss sie ankommen, in Apache lässt sich das mit mod_remoteip prüfen.

xmlrpc. Die Bot-Anfragen auf xmlrpc.php haben mich daran erinnert, wofür die Schnittstelle gern missbraucht wird: Mit system.multicall lassen sich viele Passwörter in einer einzigen Anfrage durchprobieren. Wer die Schnittstelle nicht braucht, sollte sie sperren.

Der Zettel am Monitor

Einen Einbruch habe ich nicht gefunden. Das heißt nicht, dass keiner stattgefunden hat, deshalb lese ich die Logs der nächsten Wochen mit etwas mehr Misstrauen als sonst. Was ich mir aufgeschrieben habe:

  • Nummern und Versionen bei php.net nachlesen, nicht in Zusammenfassungen.
  • Den Patchstand im Image messen: docker run --rm --entrypoint php <image> -v. Tag und Build-Datum sagen dazu nichts.
  • Auch Quellen hinken hinterher, siehe Dockerfile auf GitHub. Im Zweifel im Image nachsehen.
  • unless-stopped statt always.
  • Admin-Oberflächen nur auf 127.0.0.1, Zugriff per SSH-Tunnel.
  • Hinter dem Proxy prüfen, ob der Login-Schutz die echte Besucher-IP sieht.
  • xmlrpc.php sperren, wenn man sie nicht braucht.
  • Wer nicht sofort patchen kann: Die Notbehelfe aus der Meldung (etwa session.upload_progress.enabled=0 mit Neustart aller Worker) verringern nur die Angriffsfläche. Ein Update ersetzen sie laut php.net nicht.

Und ein Hinweis an alle, die gar keinen eigenen Server betreiben: PHP steckt auf einem großen Teil aller Webseiten, und nicht jeder Betreiber liest an einem Mittwochabend php.net. In den nächsten Tagen und Wochen sind deshalb ungepatchte Seiten unterwegs, und eine übernommene Seite kann Besuchern Schädliches unterschieben, von gefälschten Login-Fenstern bis zu manipulierten Downloads. Das ist meine Einschätzung und keine Aussage der Meldungen. Ich würde in dieser Zeit bei unbekannten Seiten etwas vorsichtiger sein: Browser und Betriebssystem aktuell halten, nichts Unerwartetes herunterladen, bei Login-Seiten genau auf die Adresse schauen und Passwörter nur dort eingeben, wo man sie wirklich eingeben wollte.

Der Blog läuft jetzt mit PHP 8.3.36 und ein bisschen weniger Selbstvertrauen.

Quellen

Ähnliche Beiträge

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert