Notfall-Mitigation für PHP2Shell: session.upload_progress deaktivieren und EOL-Backports kompilieren
Notfall-Mitigation für PHP2Shell: session.upload_progress deaktivieren und EOL-Backports kompilieren
<time datetime="2026-10-23">Geprüft am 23. Oktober 2026</time>
Wenn CVE-2026-109618 und CVE-2026-109619 – zusammen als PHP2Shell bekannt – in einer Produktionslandschaft mit Hunderten Servern auftreten, ist ein sofortiges Komplett-Upgrade selten ohne Risiko möglich. Paketabhängigkeiten, OPcache-Invalidierung, Erweiterungskompatibilität und Wartungsfenster stehen dem entgegen. Gleichzeitig bleibt die nicht authentifizierte RCE bestehen, solange die Engine nicht gepatcht ist. Dieser Leitfaden beschreibt drei erprobte Wege mit unterschiedlichem Eingriffstiefe und Risiko.
- Option A – unterbrechungsfreie Härtung über
php.ini(ca. 30 Sekunden) - Option B – Grenzfilterung über Nginx oder Cloudflare
- Option C – Upstream-Backport
1d7dec1für EOL-PHP 8.1 selbst kompilieren
1. Triage- und Entscheidungsbaum
[ ALARM: PHP2Shell (CVE-2026-109618 / 109619) ]
│
┌──────────────────┴──────────────────┐
│ Können Sie php.ini / Container │
│ direkt kontrollieren? │
└──────────────────┬──────────────────┘
│
┌────────────────┴────────────────┐
▼ JA ▼ NEIN (nur Proxy/CDN)
┌───────────────────────────┐ ┌───────────────────────────┐
│ Strategie A (bevorzugt) │ │ Strategie B (Perimeter) │
│ Callback in php.ini aus │ │ multipart in Nginx / │
│ unterbrechungsfrei, ~30 s │ │ Cloudflare blockieren │
└─────────────┬─────────────┘ └───────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PHP 8.1 und älter (EOL): Strategie C │
│ ➜ Commit 1d7dec1 kompilieren oder Vendor-Backport │
└─────────────────────────────────────────────────────────┘
Der große Vorteil von Option A: Die Schwachstelle hängt ausschließlich am RFC1867-Callback. Wird dieser deaktiviert, verschwindet die gesamte Angriffsfläche, ohne dass Anwendungscode geändert werden muss.
2. Option A: Unterbrechungsfreie Härtung über php.ini
2.1 Globale Konfiguration
; /etc/php/8.3/fpm/php.ini
; RFC1867-Tracking-Callback vollständig deaktivieren
session.upload_progress.enabled = Off
session.upload_progress.cleanup = On
Die Datei liegt je nach Distribution an unterschiedlichen Pfaden. Verlassen Sie sich nicht auf Annahmen:
php --ini
php-fpm -i | grep -i 'Loaded Configuration File'
2.2 Erzwingung auf Pool-Ebene
; /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag kann von Anwendungen nicht per ini_set() überschrieben werden. In Mandanten- oder Hosting-Umgebungen ist das entscheidend, weil sonst ein einzelner vHost die Schutzmaßnahme aushebeln könnte.
2.3 Graceful Reload und Verifikation
sudo systemctl reload php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager
# Direktes Signal, falls kein systemd verwendet wird
sudo kill -USR2 "$(cat /var/run/php-fpm.pid)"
# CLI prüfen
php -r 'echo "upload_progress=", ini_get("session.upload_progress.enabled"), PHP_EOL;'
# FPM-Konfiguration prüfen
php-fpm -tt 2>&1 | grep -i upload_progress
Nach dem Reload muss die Ausgabe leer oder 0 sein. Bei FPM sind Local Value und Master Value zu prüfen, da Pool-Konfigurationen die globale Einstellung überschreiben können.
2.4 Warum Uploads weiterhin funktionieren
session.upload_progress ist eine 2012 eingeführte Komfortfunktion für Fortschrittsbalken. Sie ist nicht Teil der eigentlichen Dateiverarbeitung. $_FILES, move_uploaded_file(), MIME-Prüfungen und Größenlimits bleiben unverändert. Moderne Single-Page-Applications nutzen die File API des Browsers und XHR-/Fetch-Fortschrittsereignisse; sie sind von der Direktive nicht abhängig.
2.5 Rückfallplan
sudo cp /etc/php/8.3/fpm/php.ini /etc/php/8.3/fpm/php.ini.bak-2026-10-23
# bei Problemen
sudo cp /etc/php/8.3/fpm/php.ini.bak-2026-10-23 /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm
Ein Graceful Reload hält bestehende Verbindungen, kann aber je nach OPcache- und Session-Konfiguration kurzzeitig Latenz erzeugen. Führen Sie die Änderung in einem Lastfenster durch und beobachten Sie die 5xx-Rate.
3. Option B: Grenzfilterung über Nginx oder CDN
Wenn Sie keinen Zugriff auf die Backend-Konfiguration haben, bleibt die Filterung am Perimeter. Sie ist eine Zwischenlösung, keine Reparatur.
# /etc/nginx/conf.d/php2shell.conf
map $http_content_type $php2shell_multipart {
default 0;
"~*multipart/form-data" 1;
}
server {
client_max_body_size 8m;
client_body_buffer_size 128k;
# Öffentliche Endpunkte ohne Upload-Funktion
location ~ ^/(api|health|static|assets)/ {
if ($php2shell_multipart = 1) {
return 415;
}
if ($arg_PHP_SESSION_UPLOAD_PROGRESS) {
return 403;
}
if ($http_php_session_upload_progress) {
return 403;
}
client_max_body_size 256k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 20s;
}
# Upload-Pfad bewusst getrennt halten
location ^~ /upload/ {
client_max_body_size 64m;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
sudo nginx -t && sudo systemctl reload nginx
Bei Cloudflare können Sie eine Custom-WAF-Regel einsetzen, die Content-Type: multipart/form-data mit dem Vorkommen von PHP_SESSION_UPLOAD_PROGRESS kombiniert. Beachten Sie jedoch, dass Angreifer Feldnamen kodieren, splitten oder in Header auslagern können. Die Regel ist ein Signal, kein sicherer Zaun. Ergänzen Sie Rate-Limits pro Quell-IP und sperren Sie wiederholte 415-Antworten zeitweise.
Ein weiterer wichtiger Punkt betrifft TLS-Terminierung und Proxy-Ketten. Wenn ein CDN die Verbindung terminiert und ungeprüft an den Origin weiterleitet, sieht der PHP-Worker die ursprüngliche Quell-IP nicht mehr. Für Rate-Limits und Fail2ban müssen Sie X-Forwarded-For oder CF-Connecting-IP nur dann vertrauen, wenn der Origin ausschließlich über den Proxy erreichbar ist. Andernfalls kann ein Angreifer die Header fälschen und Sperren umgehen. Härten Sie den Origin zusätzlich per Firewall auf die Proxy-Netze und verwerfen Sie direkte Verbindungen aus dem Internet.
4. Option C: EOL-PHP 8.1 per Commit 1d7dec1 patchen
PHP 8.1 erhält keine offiziellen Upstream-Releases mehr. Das Core-Team stellte den Minimal-Patch 1d7dec1 bereit, der zwei Änderungen vornimmt.
4.1 Inhalt des Patches
/* ext/session/session.c – Callback dauerhaft deaktivieren */
- if (!PS(rfc1867_enabled)) {
+ if (!PS(rfc1867_enabled) || true) {
return retval;
}
/* Zend/zend_alloc.c – OOM sofort beenden */
- zend_error_noreturn(E_ERROR, "Allowed memory size exhausted");
+ _exit(1);
Die zweite Änderung beendet den Worker unmittelbar bei OOM oder Integer-Überlauf. Dadurch verschwindet das Zeitfenster, in dem UAF oder Heap-Grooming möglich wäre. Der Preis ist ein harter Prozessabbruch; Availability geht hier bewusst vor Integrität. Passen Sie pm.max_children, emergency_restart_threshold und das Monitoring entsprechend an.
4.2 Build-Ablauf
sudo apt build-dep php8.1 -y 2>/dev/null || sudo dnf builddep php -y
git clone --depth 1 -b PHP-8.1-CVE-2026-109618-109619 \
https://github.com/php/php-src.git
cd php-src
./buildconf --force
./configure \
--prefix=/usr/local/php81-patched \
--enable-fpm \
--with-fpm-user=www-data \
--with-fpm-group=www-data \
--with-openssl \
--with-zlib \
--enable-mbstring
make -j"$(nproc)"
sudo make install
sudo /usr/local/php81-patched/sbin/php-fpm -t
4.3 Vor dem Produktivbetrieb prüfen
/usr/local/php81-patched/bin/php -v
/usr/local/php81-patched/bin/php -r \
'echo ini_get("session.upload_progress.enabled"), PHP_EOL;'
systemctl daemon-reload
systemctl restart php81-patched-fpm
curl -sS https://ihre-domain.example/health -o /dev/null -w '%{http_code}\n'
Ein selbst kompilierter Interpreter verlagert die Verantwortung für künftige Sicherheitsupdates vollständig auf Ihr Team. Bevorzugen Sie daher Vendor-Backports von Ubuntu Pro, CloudLinux oder Debian LTS. Der Eigenbau ist die letzte Option für Umgebungen, die aus fachlichen Gründen nicht migrieren können.
5. Betriebliche FAQ
F1: Bricht session.upload_progress.enabled = Off ältere Upload-Formulare?
Nur der Fortschrittsbalken entfällt. Erfolg, Fehlerbehandlung, Größenlimits und MIME-Prüfung bleiben unverändert.
F2: Wie überzeuge ich mich von der Wirksamkeit?
CLI: php -r 'var_dump(ini_get("session.upload_progress.enabled"));'. FPM: temporäre phpinfo()-Seite mit Local und Master Value prüfen. Ergänzend Suricata- und Falco-Regeln einsetzen, um Ausnutzungsversuche zu erkennen.
F3: Warum reicht eine WAF-Regel auf Body-Größe nicht? Reale PHP2Shell-Payloads liegen häufig bei 450 bis 500 Byte. Grenzen von 2 MB oder 5 MB greifen nicht. Die Deaktivierung auf Engine-Ebene ist der einzige zuverlässige Schnitt.
F4: Sind 8.5.12, 8.4.27, 8.3.36 und 8.2.35 vollständig sicher? Für die beiden veröffentlichten CVE enthalten sie die Korrekturen. Wenn Sie die Upload-Progress-Funktion nicht benötigen, lassen Sie sie dennoch deaktiviert, um die Angriffsfläche zu minimieren.
F5: Muss ich nach dem Patch die Konfiguration zurücksetzen? Nein. Die Deaktivierung kann dauerhaft bestehen bleiben. Dokumentieren Sie die Entscheidung, damit sie bei einem späteren Feature-Request nicht versehentlich rückgängig gemacht wird.
6. Kommunikation im Incident
Technische Maßnahmen allein genügen nicht. Informieren Sie Hosting-Kunden, interne Fachbereiche und das Management in drei Stufen: (1) Sofortmaßnahme aktiv, (2) Patch-Zeitplan und Wartungsfenster, (3) Abschlussbericht mit verbleibendem Restrisiko. Für Kunden mit eigenen PHP-Anwendungen stellen Sie ein kurzes Merkblatt bereit, das die Deaktivierung von session.upload_progress und die Prüfbefehle enthält. So vermeiden Sie Support-Lawinen und stellen sicher, dass auch mandantenspezifische Konfigurationen angepasst werden.
Dokumentieren Sie außerdem, welche Systeme nur über Option B geschützt sind. Diese Systeme haben ein höheres Restrisiko und sollten im Patch-Backlog priorisiert werden. Ein klarer Statusbericht pro Server – gepatcht, mitigiert, ungeschützt – ist die Grundlage für jede weitere Entscheidung.