Seit dem 21. Oktober geistert PHP2Shell durch die Newsticker. Hinter dem griffigen Namen stecken zwei Lücken in PHP, CVE-2026-109618 und CVE-2026-109619, über die Angreifer ohne Anmeldung per präpariertem HTTP-Request Code auf dem Webserver ausführen können. Die PHP-Entwickler stufen beide mit 9.2 nach CVSS 4.0 ein, und laut watchTowr wird die Lücke bereits aktiv ausgenutzt. Wer selbst hostet, sollte also jetzt nachschauen.
Abgedichtet sind die Lücken in PHP 8.5.12, 8.4.27, 8.3.36 und 8.2.35. Für PHP 8.1 und älter gibt es bislang keine regulären Releases mit dem Fix, da hilft nur der Umstieg auf einen unterstützten Zweig. Auf einem normalen Server erledigt das der Paketmanager. Bei Docker sieht die Sache anders aus: Jeder Container bringt sein eigenes PHP mit, und das bleibt so alt, wie das Image eben ist.
Welcher Container hat welches PHP?
Die erste Frage lautet also: In welchen Containern auf dem Heimserverchen steckt überhaupt ein PHP? Bei Nextcloud oder WordPress ist das klar, bei selbst gebauten Images oder kleinen Web-Apps weniger. Statt jeden Container einzeln abzuklappern, fragt die folgende Schleife alle laufenden Container nach ihrer PHP-Version und gleicht sie gleich mit den gepatchten Versionen ab.
for c in $(docker ps --format '{{.Names}}'); do
v=$(docker exec "$c" php -r 'echo PHP_VERSION;' 2>/dev/null) || continue
case "${v%.*}" in
8.5) fix=8.5.12 ;;
8.4) fix=8.4.27 ;;
8.3) fix=8.3.36 ;;
8.2) fix=8.2.35 ;;
*) fix=99 ;;
esac
if [ "$(printf '%s\n' "$fix" "$v" | sort -V | head -n1)" = "$fix" ]; then
printf '\e[32m%-30s %-8s ok\e[0m\n' "$c" "$v"
else
printf '\e[31m%-30s %-8s anfällig?\e[0m\n' "$c" "$v"
fi
doneContainer ohne PHP überspringt die Schleife stillschweigend. Bei allen anderen landet die Version grün in der Ausgabe, wenn sie mindestens auf dem Stand des Patches ist, und rot, wenn sie darunter liegt. Rot gibt es auch für PHP 8.1, 7.x und älter, denn dafür existiert schlicht kein Fix. Bei mir sieht das Ergebnis so aus:
./php2shell-simple.sh
nextcloud-app-1 8.5.12 ok
nextcloud-cron-1 8.5.12 ok
linuxundich-test-wordpress-1 8.3.36 ok
planetfoss-web 8.3.33 anfällig?
sisu-web 8.4.26 anfällig?Nextcloud und das Test-WordPress hatten ihre Updates also schon gezogen, PlanetFOSS und die Seite für Sisu hingen dagegen noch hinterher. Das Fragezeichen hinter „anfällig“ ist übrigens Absicht. Die Schleife vergleicht nur die Versionsnummer. Nutzt ein Image das PHP-Paket von Debian, Ubuntu oder Alpine, kann der Fix zurückportiert sein, ohne dass sich die Nummer ändert. Dann hilft nur ein Blick in die Sicherheitsmeldungen der Distribution.

Genauer hinschauen lässt sich mit php2shell-check von EXT IT. Das MIT-lizenzierte Skript arbeitet nur lesend und bewertet PHP anhand der Paketversionen statt nur der Versionsnummer, berücksichtigt also auch zurückportierte Fixes. Es schaut in Docker-Container, FPM-Pools und noch laufende alte Prozesse und schickt auf Wunsch eine harmlose Anfrage an eure Website. Für die Container braucht es Root-Rechte oder die Mitgliedschaft in der Gruppe docker.
Die Projektseite schlägt den üblichen Einzeiler mit curl und sudo bash vor. Bei einem Skript, das mit Root-Rechten auf dem Server läuft, mag ich das nicht so gern. Ladet lieber das Skript samt SHA256SUMS aus den Releases herunter, prüft es mit sha256sum -c SHA256SUMS, lest einmal drüber und startet es dann von Hand. Ein „nicht anfällig“ ist laut den Entwicklern übrigens keine Garantie:
sudo bash php2shell-check.sh https://eure-seite.exampleAuf meinem Heimserver lief das Skript mit sudo. Als Mitglied der Gruppe docker sähe es die Container zwar auch ohne Root-Rechte, laufende Prozesse anderer Benutzer und manche Logs blieben dann aber verborgen. Geholt und gestartet habe ich es so:
curl -fsSLO https://github.com/EXT-IT/php2shell-check/releases/latest/download/php2shell-check.sh
chmod +x php2shell-check.sh
sudo ./php2shell-check.sh Das Urteil deckt sich mit der Schleife: planetfoss-web mit PHP 8.3.33 und sisu-web mit PHP 8.4.26 sind anfällig, Nextcloud und das Test-WordPress nicht. Zusätzlich hat es einen gestoppten Container von Tuxsucht gefunden, den meine Schleife glatt übersehen hat:
PHP in docker containers
[ OK ] container nextcloud-app-1 (nextcloud:35-apache)
PHP 8.5.12 (fixed from 8.5.12)
[...]
[VULN] container planetfoss-web (planetfoss-web:local)
PHP 8.3.33, fixed in 8.3.36
-> Update PHP to 8.3.36 or later (container: pull the new image and recreate)
[...]
[VULN] container sisu-web (sisu-web:local)
PHP 8.4.26, fixed in 8.4.27
[...]
[WARN] container php83 (stopped, tuxsucht-php)
stopped, but its image ships PHP 8.3.33: it comes back vulnerable on the next start
[...]
Summary 2 vulnerable, 3 undecided, 12 warnings, 11 okKlar, docker ps listet eben nur laufende Container, und ein gestoppter Container nimmt keine Anfragen an. Startet ihn aber irgendwann jemand wieder, ist die Lücke sofort zurück. Deutlich wird das Skript außerdem beim Thema mod_php: Alle fünf laufenden Container liefern PHP über das Apache-Modul aus, und genau gegen dieses Setup demonstriert das Advisory einen kompletten Angriff ohne Anmeldung.

Bis zum Update schlägt das Skript als Notbehelf vor, session.upload_progress.enabled=0 und expose_php=0 zu setzen und post_max_size unter einem Viertel von memory_limit zu halten. Die Prüfung von außen hat dagegen wenig gebracht. Für die beiden Websites meldet das Skript nur, dass die Header keine PHP-Version verraten und keine WAF davorsteht. Über den Patchstand sagt das nichts, was das Skript auch ehrlich zugibt.
Und wie bekommt man das PHP aktualisiert?
Im Container selbst solltet ihr nichts aktualisieren, das wäre beim nächsten Neustart wieder weg. Stattdessen zieht ihr im jeweiligen Compose-Verzeichnis das neue Image und baut den Container damit neu auf. Bei selbst gebauten Images mit eigenem Dockerfile braucht es ein docker compose build --pull, damit Docker auch das Basis-Image frisch holt:
docker compose pull && docker compose up -dWichtig: Das hilft natürlich nur, wenn das Basis-Image die gepatchte Version auch schon enthält. Laut IT-Connect tauchten die neuen Tags anfangs sogar ohne Image dahinter auf, inzwischen liefern allgemeine Tags wie php:8.3-apache das gepatchte PHP. Wer dagegen eine feste Version wie php:8.4.24-fpm im Dockerfile stehen hat, muss sie selbst anheben. Lasst die Schleife nach dem Update einfach noch einmal laufen. Kümmert euch zuerst um die Container, die aus dem Internet erreichbar sind.
Wie das in der Praxis aussieht, zeigt die Testumgebung von PlanetFOSS. Das Image baut auf php:8.3-apache auf und packt im Dockerfile nur noch Erweiterungen wie GD und Intl dazu. Mein Deploy-Skript ruft zwar docker compose up -d --build auf, holt dabei aber kein neues Basis-Image, sondern nimmt das vom August aus dem Cache. Daher stammte das alte PHP 8.3.33. Erst --pull lädt die frische Fassung:
cd /data/docker/planetfoss
docker compose build --pull web
docker compose up -d webNach gut einer Minute stand das neue Image, und der Container lief mit PHP 8.3.36. Die Datenbank im selben Compose-Projekt bleibt davon unberührt, weil beide Befehle nur den Dienst web ansprechen. Die Schleife zeigt PlanetFOSS danach grün, und auch php2shell-check meldet nur noch sisu-web als anfällig. Das alte Image liegt übrigens noch als Leiche auf der Platte, docker image prune räumt es weg.
./php2shell-simple.sh
[...]
nextcloud-cron-1 8.5.12 ok
linuxundich-test-wordpress-1 8.3.36 ok
sisu-web 8.4.26 anfällig?Alles in allem ist PHP2Shell ein gutes Beispiel dafür, dass Selfhosting mit Docker zwar bequem ist, die Updates aber nicht von allein kommen. Weitere Details zu den Lücken hat heise zusammengetragen. Wie man Container ohne viel Gefrickel auf Stand hält, habe ich mir übrigens in Docker-Container einfach aktuell halten mit Dockcheck angeschaut. Wie haltet ihr eure Container aktuell?




Schreibe einen Kommentar