Zum Inhalt springen
← Alle Artikel

php2shell: PHP patcht zwei RCE-Lücken im Kern

21. Okt. 2026 · Backend · 6 min

·Von Dimitri Pisarev

PHP hat heute um 14:00 UTC ein Security-Release außer der Reihe ausgeliefert: 8.5.12, 8.4.27, 8.3.36 und 8.2.35 schließen zwei unauthentifizierte Remote-Code-Execution-Lücken direkt in der Engine, CVSS v4.0 9.2, mit vollständig demonstrierter Ausnutzung gegen Apache mod_php in den Standardeinstellungen, bevor irgendein Code der Anwendung läuft (Ankündigung auf php.net). Jede PHP-Installation, die Web-Requests bedient, ist betroffen, und die Veröffentlichung verlief chaotisch genug, dass Angreifer einen Tag Vorsprung hatten.

Was sind die zwei Schwachstellen?#

CVEMechanismusBestätigte Exposition
CVE-2026-109618OOM-Korruption: longjmp() aus teilweise initialisiertem Request-Zustandvollständige RCE auf mod_php mit Standard-Limits; FPM über Anwendungspfade angreifbar
CVE-2026-109619veralteter globaler Zustand: ein späterer Request nutzt freigegebenen SpeicherUse-after-free bestätigt; RCE gegen eine echte Anwendung auf mod_php

Die erste ist ein Speicher-Limit-Problem. PHP nutzt longjmp(), um einen Request abzubrechen, der memory_limit erreicht, und in manchen Parsing-Zuständen ist der Request-Speicher erreichbar, bevor er vollständig initialisiert ist. Ein Allokationsfehler in diesem Moment hinterlässt ungültige Daten, und die Shutdown-Prozedur liest sie anschließend oder verzweigt auf ihnen. Angreifer steuern das Limit über Eigenheiten des Multipart-Parsings, die das Advisory auflistet: Ein Request ohne brauchbare Content-Length passiert die post_max_size-Prüfung, ein einzelner Part kann genug Header tragen, um seine Übertragungsgröße im PHP-Speicher zu vervielfachen, und leere File-Controls bauen Metadata-Arrays, ohne Richtung max_file_uploads zu zählen. Für das Triage-Listing ist die SAPI-Rangfolge wichtig. mod_php und FrankenPHP führen das Request-Shutdown bedingungslos aus, die Korruption passiert also vor dem Application-Code, und eine vollständige RCE wurde über HTTP mit Standard-Limits validiert. FPM beendet den Child-Prozess, wenn das Startup fehlschlägt, was diese Tür schließt; ein vollständiger FPM-Exploit wurde nicht demonstriert, die Quellenanalyse hält FPM dennoch für angreifbar. Der Patch entfernt longjmp() für OOM vollständig und ersetzt es durch ein sanftes Limit am nächsten VM-Safe-Point und ein hartes _exit() oberhalb einer zweiten Schwelle.

Warum die zweite Lücke deine Error-Einstellungen ignoriert#

Die zweite Schwachstelle ist ein Use-after-free über Requests desselben Workers hinweg, und ihre Anatomie erklärt die ungewohnte Mitigations-Liste. Eine Producer-Operation, ein fataler Fehler, ein Timeout, ein laufender Upload, hinterlässt Request-eigenen Zustand in einem globalen Feld, nachdem der Request-Heap freigegeben wurde. Eine Consumer-Operation im nächsten Request berührt diesen Zustand, bevor die normale Initialisierung ihn ersetzt. Das Advisory bestätigt zwei Consumer, und beide zu verstehen lohnt sich.

Der erste Consumer ist eine INI-Diagnose. Eine Warnung, die beim Anwenden der per-Request-Konfiguration entsteht, eine ungültige oder veraltete Einstellung, erreicht den veralteten Fehlerzustand, bevor der Executor initialisiert. Der überraschende Teil: Der globale Zustand wird konsultiert, bevor PHP entscheidet, ob die Warnung angezeigt, geloggt oder unterdrückt wird. error_reporting, display_errors und der @-Operator schützen also überhaupt nicht. Auf mod_php kommt erschwerend hinzu, dass .htaccess und Apache-php_value-Direktiven vor dem Request-Startup angewendet werden. Der zweite Consumer ist session.upload_progress, seit PHP 5.4 standardmäßig aktiviert, das Session-Zustand vor der normalen Session-Initialisierung erreichen kann. Der stärkste demonstrierte Fall sitzt auf PHP 8.5: Mit aktiviertem fatal_error_backtraces veröffentlicht das Fatal-Error-Reporting Backtrace-Zustand, den das Shutdown vergessen zurückzusetzen, und dieser Pfad ist die Grundlage der bestätigten mod_php-Ausnutzung.

Die Veröffentlichung geriet aus der Bahn#

Das Release war für den 22. Oktober angekündigt, dann kippte der Zeitplan. Am 20. Oktober gegen 15:00 UTC lekten die beiden CVE-Nummern in Foren, versehen mit einem falschen CVSS von 10.0, woraufhin das Projekt seine Vorankündigung auf 19:30 UTC vorzog und das Release um einen Tag nach vorn zog. Gegen 20:00 UTC veröffentlichte die Security-Firma Calif versehentlich ein Video ihres Proof of Concept gegen echte Ziele, und Kopien kursierten, bevor die Löschung etwas bedeutete. Am Release-Morgen um 07:41 UTC, sechs Stunden vor der koordinierten Veröffentlichung, lieferte Chainguard Container-Images mit dem Fix aus und brach das Embargo, womit Angreifer einen Binär-Diff bekamen, während alle anderen warteten. Das PHP-Security-Team hat die daraus entstandene PoC-Aktivität beobachtet und zum Zeitpunkt der Veröffentlichung keinen funktionsfähigen Exploit im Umlauf identifiziert. SANS hat wegen der Verbreitung von PHP den Infocon auf Gelb angehoben, und die Messung von VirusTotal macht die Reichweite konkret: rund 5,1 Millionen IP-Adressen laufen auf PHP, vier von fünf verbergen ihre Version, und von denen, die eine offenlegen, sitzen rund 758.000 auf End-of-Life-Zweigen von 5.4 bis 8.1, während nur etwa 272.000 einen unterstützten Zweig fahren. Laravel taucht auf etwa 141.000 dieser Hosts auf.

Ein Fix, der zwei Jahre brauchte#

Die Entstehungsgeschichte ist die unbequeme Hälfte. Das OOM-Problem tauchte erstmals in PHPs externem Audit 2024 von Quarkslab auf, finanziert von der Sovereign Tech Agency. Ein Exploit wurde damals theoretisiert, aber das Security-Team, arbeitend im Konsens zwischen konkurrierenden Fix-Ansätzen, die jeweils etwas kaputt machten, brauchte zwei Jahre. Der Bericht der PHP Foundation nennt die Verzögerung „a structural failure, caused by both the security team's decision process and its lack of resources“, ungewohnt deutliche Worte für einen Projektbeitrag. Den Stillstand brach ein Wellenschlag von außen: Über OpenAIs Programm Patch the Planet meldete der Forscher Ryan A bei der Security-Firma Calif am 11. August eine bedingungslose RCE mit derselben Ursache, OpenAI meldete am selben Tag dieselbe Wurzel, und Wiz ergänzte am 24. August weitere Vektoren. Das Team hat seit dem 1. Juli außerdem 15 CVEs veröffentlicht und über hundert ungültige Reports durchgearbeitet, eine Triage-Last, die mit KI-gestützter Schwachstellenforschung ankam; KI hilft beim Validieren, jede Entscheidung und jeder Patch geht durch einen Menschen.

Was zu tun ist, in dieser Reihenfolge#

Aktualisieren zuerst: 8.5.12, 8.4.27, 8.3.36 oder 8.2.35, oder das Paket der Distribution, da Distros und LTS-Anbieter die Patches vorab erhalten haben, danach jeden Worker neu starten. PHP 8.1 und älter erhalten keine Projekt-Releases mehr, aber das Security-Team hat minimale Source-Backports für 7.4, 8.0 und 8.1 veröffentlicht, was etwas über die Zahl der Installationen dort aussagt.

Wenn das Update heute nicht landet, ordnen die Advisories die Übergangslösungen, und jede deckt einen konkreten Pfad. memory_limit=-1 per php_admin_value erzwingen, sodass kein Pool, Verzeichnis oder Anwendungseinstellung den endlichen Grenzwert zurückholt, post_max_size auf 8M ohne Uploads oder 32M mit Uploads begrenzen, session.upload_progress.enabled=0 in die php.ini setzen und auf 8.5 fatal_error_backtraces=0, in jedem Fall die Worker neu starten. Angreifbar erreichbare fatale Fehler und Timeouts in der Anwendung beheben, denn sie sind jetzt Producer. Ein Verbot verdient eine eigene Zeile: RLIMIT_AS oder RLIMIT_DATA nicht als Speichergrenze verwenden, denn sie erzeugen den Allokationsfehler, von dem die erste RCE lebt. Ein cgroup-Limit ist die akzeptable Grenze, wenn jeder Worker eine eigene Gruppe bekommt.

Der Laravel-Winkel ist eine Frage des Zeitpunkts, nicht des Frameworks. Die Lücke sitzt im Kern, Composer-Versionen und Laravel-Releases ändern an der Exposition nichts, und Laravel Forge hat eine eigene Anleitung für die von ihm verwalteten Server veröffentlicht. Die Laravel-Systeme, die auf dieser Seite dokumentiert sind, etwa die Banking-Backend-Architektur, laufen auf exakt diesem Runtime. Beide Runtimes, auf die ich liefere, brauchten diese Woche einen Notfall-Patch, Next.js am Montag und PHP heute, und der PHP-Fall ist der dringendere mit deutlichem Abstand.