Il 20 ottobre 2026 sono state rese pubbliche due falle gravi nel core di PHP. Il nome comune è PHP2Shell e gli identificativi sono CVE-2026-109618 e CVE-2026-109619. Il punteggio CVSS è 9,2. Permettono di eseguire codice sul server senza autenticarsi, e gli attacchi sono già stati osservati.

In questo articolo spiego cosa sono e quali versioni di PHP sono coinvolte. Racconto come ho messo in sicurezza i server che gestisco mentre la patch non c’era, e come sono arrivato all’aggiornamento definitivo. Trovi anche lo script che ho usato e i comandi per lanciarlo.
Cos’è PHP2Shell
Il problema sta nel modo in cui PHP legge le richieste di tipo multipart/form-data. È il formato che il browser usa per inviare i file. PHP lo interpreta prima che parta qualsiasi script.
Per questo non serve un modulo di upload. Può bastare una sola richiesta a un qualsiasi file PHP del sito.
Le due falle, in sintesi:
- CVE-2026-109618. Se la memoria si esaurisce mentre PHP legge la richiesta, alcuni dati si corrompono. La corruzione emerge nella pulizia finale della richiesta e può portare a eseguire codice.
- CVE-2026-109619. Parte dello stato di una richiesta resta in memoria. Una richiesta successiva, sullo stesso processo, lo riusa.
Questi dettagli tecnici vengono dall’analisi del NCSC olandese e da altre fonti pubbliche. Non ho provato a sfruttare le falle sui miei server.
Quali versioni di PHP sono coinvolte
Le versioni corrette sono uscite il 21 ottobre 2026: PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35. Ogni versione precedente a queste è colpita.
I rami 8.1 e più vecchi non hanno una versione corretta ufficiale, perché sono fuori supporto. PHP ha pubblicato solo dei backport minimi del codice sorgente per 7.4, 8.0 e 8.1. Per rami ancora più vecchi non risulta nulla.
Il rischio riguarda ogni modo di eseguire PHP. Il più esposto è mod_php. Contano anche PHP-FPM, php-cgi, FrankenPHP e LiteSpeed. Riguarda poi ogni applicazione che gira sopra: WordPress, Joomla, Drupal, Nextcloud, phpMyAdmin, Roundcube e il pannello ISPConfig.
Su Debian c’è un dettaglio da conoscere. Debian applica le correzioni come backport e non cambia il numero di versione di PHP. Un server può mostrare un numero basso ed essere già corretto. Per questo lo script non confronta le versioni. Controlla se il changelog del pacchetto installato cita le due CVE.
La mitigazione, in attesa della patch
Quando le falle sono diventate pubbliche, le patch non erano ancora disponibili per le distribuzioni. Non volevo lasciare i server scoperti, quindi ho ridotto il rischio con uno script.
La mitigazione non è una patch. Disattivare session.upload_progress chiude solo uno dei percorsi noti della CVE-2026-109619 e non fa nulla contro la CVE-2026-109618. Serviva a restringere il margine di attacco fino all’aggiornamento, non a sostituirlo.
Cosa fa lo script
Lo script è pensato per Debian 12 e 13. Funziona con Apache (mod_php, FastCGI, suPHP, PHP-FPM) e con Nginx (PHP-FPM). Riconosce i server gestiti con ISPConfig, sia Apache sia Nginx.
Le opzioni:
- Senza opzioni controlla e basta. Per ogni pacchetto PHP verifica che il changelog citi le due CVE. Segnala i processi che usano ancora un binario vecchio, legge le impostazioni attive e le porte web in ascolto.
--updateaggiorna prima l’indice dei pacchetti.--logscerca indicatori sospetti: file PHP recenti nelle cartelle di upload o cache, pattern tipici delle webshell, riferimenti a php2shell nei log. È un’euristica e non prova una compromissione.--hardenapplica la messa in sicurezza descritta qui sotto.--dry-run, insieme a--harden, mostra cosa farebbe senza modificare nulla.--skip-disable-functions, insieme a--harden, non toccadisable_functions.--rollbacktoglie quanto applicato da--harden.
Cosa cambia con –harden
Per ogni versione di PHP presente in /etc/php, lo script crea il file mods-available/cve-hardening.ini. Lo abilita con phpenmod per i SAPI fpm, apache2 e cgi. Così la protezione vale per tutti i siti in una volta.
I php.ini originali restano intatti. La CLI non viene toccata, quindi composer, artisan e wp-cli continuano a funzionare.
Il file imposta tre cose:
session.upload_progress.enabled = Offexpose_php = Offdisable_functionscon exec, passthru, shell_exec, system, proc_open, popen e pcntl_exec, aggiunte a quelle già presenti.
Poi lo script testa la configurazione e ricarica PHP-FPM e Apache solo se il test passa. Prima di scrivere fa un backup in /var/backups/php-cve-hardening/.
I limiti da conoscere
disable_functionspuò rompere alcune applicazioni, per esempio WordPress con certi plugin o Magento. Se succede, usa--rollbackoppure rilancia con--skip-disable-functions.- Le direttive
php_admin_valuenei pool PHP-FPM hanno la precedenza sul file globale. Lo script le segnala ma non le modifica. - Con ISPConfig i file in
pool.dsono rigenerati dal pannello. Gli override vanno corretti dal pannello, in Sites, Website, Options, Custom php.ini settings, non a mano. - I
php.iniper singolo sito (modalità FastCGI o suPHP di ISPConfig) e le versioni PHP in/optnon sono gestiti. Vanno controllati a parte.
Come lanciare lo script
Lo script è qui: https://cisilino.com/mscripts/php20261.sh
Prima di eseguire qualsiasi cosa da internet, leggila. Puoi scaricarlo e aprirlo:
wget -qO php20261.sh https://cisilino.com/mscripts/php20261.sh
less php20261.sh
Se vuoi lanciarlo direttamente da remoto, servono i privilegi di root. Questi sono i comandi, nell’ordine che consiglio.
1. Solo controllo, con aggiornamento dell’indice dei pacchetti:
wget -qO- https://cisilino.com/mscripts/php20261.sh | sudo bash -s -- --update
2. Simulazione della messa in sicurezza, senza modifiche:
wget -qO- https://cisilino.com/mscripts/php20261.sh | sudo bash -s -- --harden --dry-run
3. Applicazione:
wget -qO- https://cisilino.com/mscripts/php20261.sh | sudo bash -s -- --harden
4. Rimozione, se qualcosa si rompe:
wget -qO- https://cisilino.com/mscripts/php20261.sh | sudo bash -s -- --rollback
Dopo --harden e dopo --rollback non serve riavviare nulla a mano: lo script ricarica da solo PHP-FPM e Apache, dopo aver testato la configurazione. Il riavvio serve invece dopo l’aggiornamento del pacchetto PHP, perché i processi in esecuzione tengono in memoria il binario vecchio:
sudo systemctl restart 'php*-fpm' apache2
Gli apici attorno a php*-fpm servono. Senza, è la shell a espandere l’asterisco, non systemctl. Se il server usa solo Nginx, togli apache2.
L’esito dello script è anche un codice di uscita: 0 se è tutto a posto, 1 se ci sono avvisi, 2 se il sistema è vulnerabile o serve un’azione.
Sui server con ISPConfig: ispconfig-multipart-guard
Chi usa ISPConfig ha un’arma in più. Il progetto ha rilasciato lo script ispconfig-multipart-guard, descritto nell’avviso ufficiale di ISPConfig.
Il principio è semplice. Se il problema è nelle richieste multipart, il web server le ferma prima che arrivino a PHP:
- blocca le richieste
Content-Type: multipart/...per tutti i siti, per alcuni siti scelti o per il pannello di controllo; - con Nginx risponde 415 alle richieste PHP dei siti bloccati e i file statici non cambiano. Con Apache risponde 403, anche per i file statici;
- di default blocca solo il pannello ISPConfig. I siti vanno bloccati con comandi come
block alloblock esempio.it; - crea un cron che ogni 5 minuti ripristina i suoi hook, se ISPConfig riscrive la configurazione;
- ha una lista di IP autorizzati che possono ancora caricare file.
Funziona con Debian e Ubuntu, con ISPConfig 3.2 o 3.3 (e anche senza ISPConfig), con Nginx o con Apache 2.4 e successivi.
Il prezzo è che sui siti bloccati gli upload non funzionano. Valgono anche i media dei CMS, gli allegati dei moduli e quelli del webmail, e gli import di phpMyAdmin. Le pagine normali e i moduli urlencoded continuano a funzionare. Anche questa è una misura provvisoria e non una correzione.
Questo script e il mio non sono alternativi. Il mio agisce sulla configurazione di PHP, quello di ISPConfig sul web server. Non li ho confrontati sul campo e non posso dirti come si comportano insieme su ogni configurazione.
L’aggiornamento definitivo, in due passaggi
Le mitigazioni dovevano durare poco. Ho aggiornato i server non appena gli aggiornamenti sono stati disponibili, in due passaggi:
- Compilazione diretta. In attesa che la distribuzione in uso sul singolo server rilasciasse il suo pacchetto, ho compilato PHP dai sorgenti nelle versioni corrette.
- Pacchetto ufficiale. Quando il pacchetto ufficiale della distribuzione è stato rilasciato per quel server, ho sostituito la compilazione con il pacchetto.
Il secondo passaggio conta. Un PHP compilato a mano non si aggiorna da solo con il resto del sistema. Il pacchetto della distribuzione sì, e lo script può verificarlo leggendo il changelog.
Cosa fare adesso
- Aggiorna PHP alla 8.5.12, 8.4.27, 8.3.36 o 8.2.35. Su Debian puoi anche usare il pacchetto della distribuzione quando il changelog cita le due CVE.
- Riavvia PHP-FPM e Apache, poi rilancia lo script senza opzioni. Non deve più segnalare problemi.
- Se usi PHP 8.1 o precedente, pianifica il passaggio a un ramo supportato. Per quei rami non c’è una versione ufficiale corretta.
- Tieni disattivato
session.upload_progressfino al prossimo rilascio di correzioni, se la tua applicazione non lo usa. - Controlla i log e i file recenti, con
--logso a mano, cercando file PHP comparsi nelle cartelle di upload. - Non dimenticare PHP dentro container, immagini e appliance di terzi. Vanno aggiornati a parte.
- Se il tuo sito usa WordPress, il plugin Cisilino Site Defense Network non risolve PHP2Shell. Protegge però il login e tiene un registro delle attività, utile per capire cosa succede sul sito.
Fonti
- ISPConfig: PHP2Shell, vulnerabilità critiche di PHP (CVE-2026-109618, CVE-2026-109619)
- NCSC-NL: wiki su PHP2Shell
ISPConfig, PHP, Debian e gli altri nomi citati sono marchi dei rispettivi proprietari. Sono indicati solo per identificare i prodotti.

Lascia un commento