CrowdStrike protegge i clienti dalle minacce veicolate tramite PHP2Shell
Sono state divulgate due vulnerabilità critiche di esecuzione di codice da remoto (RCE), causate da errori di corruzione della memoria nel core di PHP e identificate come CVE-2026-109618 e CVE-2026-109619.
21 ottobre 2026 • Amit Serper • Dalla prima linea
Sono state divulgate due vulnerabilità critiche di esecuzione di codice da remoto (RCE), causate da errori di corruzione della memoria e denominate collettivamente PHP2Shell. Le vulnerabilità interessano il core di PHP, il motore alla base del funzionamento di una vasta parte del web. La piattaforma CrowdStrike Falcon® offre visibilità e funzionalità di rilevamento immediate per identificare le attività successive allo sfruttamento di entrambe le vulnerabilità.
Tracciate con gli identificativi CVE-2026-109618 e CVE-2026-109619, entrambe le vulnerabilità hanno un punteggio CVSS 4.0 pari a 9,2 (critico) e sono classificate come CWE-824: accesso a puntatore non inizializzato. Entrambe consentono a un avversario remoto non autenticato di eseguire comandi di sistema arbitrari su un server web interessato tramite HTTP.
I due problemi hanno un'unica causa alla radice. Quando PHP si trova in una condizione di errore fatale, ad esempio al superamento del limite di memoria configurato memory_limit, o del tempo massimo di esecuzione, oppure in caso di interruzione della connessione con il client, non propaga l’errore lungo la catena delle chiamate delle funzioni. PHP Genera un errore fatale e usa longjmp() per abbandonare del tutto il contesto di esecuzione corrente, interrompendo la richiesta. Qualsiasi funzione C interrotta mentre sta costruendo una struttura dati viene semplicemente scartata: non vengono eseguiti distruttori, non vengono ripristinate le informazioni di gestione dello stato interno e non rimane alcuna traccia del fatto che la struttura dati sia incompleta. PHP procede quindi con una normale sequenza di pulizia che presuppone che tutte le strutture siano in uno stato coerente.
Le due CVE descrivono in quali casi tale presupposto viene meno:
- CVE-2026-109618 (RCE tramite out-of-memory) riguarda i contesti in cui la memoria della richiesta rimane non inizializzata. Una struttura dati viene contrassegnata come contenente una voce prima dell'assegnazione della memoria corrispondente; se tale assegnazione supera il limite di memoria, la voce conserva dati residui nell'heap. La routine di pulizia della richiesta non riuscita legge quindi quei byte ed esegue diramazioni logiche in base a essi.
- CVE-2026-109619 (RCE tramite errori fatali) riguarda le situazioni in cui un errore fatale lascia lo stato globale del sistema in una condizione non valida. Una variabile globale a livello di modulo continua a puntare all'heap relativo alla singola richiesta che lo shutdown ha già rilasciato e continua a indicare che l'operazione è attiva. Una richiesta successiva gestita dallo stesso worker legge quella memoria rilasciata o esegue diramazioni logiche in base al suo contenuto.
In entrambi i casi, l'autore dell'attacco controlla il trigger e, cosa fondamentale, i dati che PHP finisce per interpretare in modo errato. CrowdStrike ha verificato end-to-end che una singola richiesta POST anonima può indurre un worker di Apache mod_php a lanciare un processo shell ed eseguire un comando specificato dall'autore dell'attacco, senza alcuna autenticazione, senza sessione e senza che sia coinvolto alcun codice applicativo vulnerabile.
A rendere più gravi queste vulnerabilità è la loro collocazione nello stack di esecuzione. PHP analizza i dati delle richieste in ingresso prima che vengano inizializzate le estensioni e prima ancora che venga compilata una sola riga di codice dell'applicazione, quindi lo sfruttamento è indipendente da ciò che ospita il server. Un'applicazione sottoposta a manutenzione scrupolosa è esposta esattamente allo stesso livello di un'applicazione trascurata. L'attacco è anche sorprendentemente silenzioso: il processo worker non va in crash, il server restituisce una normale risposta HTTP 200 e i log dell'applicazione non registrano nulla di insolito.
Durante i nostri test abbiamo sviluppato un codice exploit in grado di funzionare in modo affidabile su entrambe le architetture x86-64 e ARM, mantenendo la propria affidabilità anche dopo i riavvii dei worker. Sui sistemi basati su ARM, sempre più diffusi nelle infrastrutture cloud, l’exploit è inoltre riuscito ad aggirare il meccanismo di protezione Address Space Layout Randomization (ASLR) utilizzando esclusivamente richieste HTTP.
Dettagli tecnici
La causa comune delle vulnerabilità: longjmp() senza unwinding
Entrambe le vulnerabilità risiedono in Zend, il motore alla base di PHP. Zend è l'implementazione in C che compila il codice sorgente PHP in bytecode, lo esegue in una macchina virtuale e gestisce tutto ciò di cui il linguaggio ha bisogno durante l'esecuzione: variabili, array, oggetti, conteggio dei riferimenti e allocazione della memoria. Scritto originariamente da Zeev Suraski e Andi Gutmans (il nome è una crasi dei loro nomi di battesimo), e introdotto con PHP 4, non è un componente opzionale né un'estensione di terze parti: è incluso in ogni build di PHP, per questo il suo codice sorgente si trova nella directory Zend/ di php-src. In pratica, questo significa che un difetto in Zend è un difetto nel motore PHP stesso e interessa ogni applicazione, framework ed estensione eseguiti sull'interprete, indipendentemente da come è scritto il codice.
Due componenti fondamentali del motore Zend sono al centro delle questioni qui descritte: la HashTable, alla base di ogni array PHP, compresa la superglobale $_POST, e lo Zend Memory Manager (Zend MM), l'allocatore di PHP per ogni richiesta, che si interpone tra PHP e il sistema operativo. Il percorso di gestione degli errori fatali di PHP termina in zend_bailout(), che esegue un LONGJMP per uscire dallo stack di chiamate C corrente:
// Zend/zend.c
ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno)
{
...
CG(unclean_shutdown) = 1;
...
LONGJMP(*EG(bailout), FAILURE);
}
Non viene eseguito nulla tra l'errore fatale e il blocco zend_catch che lo racchiude. Qualsiasi funzione chiamante che abbia modificato lo stato e abbia fatto affidamento sul raggiungimento del proprio codice di pulizia lascia invariato tale stato. Ciò è sicuro solo se ogni funzione chiamante di questo tipo è scritto tenendo conto della possibilità di una terminazione improvvisa, ma molti non lo sono.
CVE-2026-109618: memoria di richiesta non inizializzata, utilizzata durante la stessa richiesta
L'istanza canonica è _zend_hash_str_add_or_update_i() in Zend/zend_hash.c, la funzione helper alla base di ogni inserimento in un array PHP con chiave stringa. Nel percorso di inserimento, incrementa entrambi i contatori degli elementi, acquisisce un puntatore al nuovo bucket e solo allora alloca una copia della chiave:
// Zend/zend_hash.c
add_to_hash:
idx = ht->nNumUsed++;
ht->nNumOfElements++;
p = ht->arData + idx;
p->key = key = zend_string_init(str, len, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
Se zend_string_init() supera memory_limit, Zend MM genera un errore fatale che termina con zend_bailout(). L'esecuzione non riprende mai per completare il bucket. A questo punto, la tabella riporta una voce in più rispetto a quelle che contiene effettivamente. Il valore, l'hash e il puntatore alla chiave di quella voce corrispondono ai byte che l'allocatore ha lasciato per ultimi in quella memoria; l'array dei bucket viene allocato con un emalloc() non inizializzato e solo l'hash region viene resettato esplicitamente.
Durante la fase di teardown, php_request_shutdown() distrugge le variabili superglobali e zend_array_destroy() itera i bucket nNumUsed. La voce obsoleta viene passata a i_zval_ptr_dtor(), che tratta i byte residui come un valore con conteggio dei riferimenti: dereferenzia il puntatore in essi contenuto, decrementa il conteggio dei riferimenti a quell'indirizzo e (se raggiunge lo zero) esegue il dispatch tramite una tabella di puntatori a funzione indicizzata da un tag di tipo proveniente anch'esso dai byte residui.
Per attivare questa condizione basta un inviare il corpo di una richiesta POST con codifica nell'URL. PHP popola la variabile $_POST all'interno della funzione php_hash_environment(), richiamata da php_request_startup() prima dell'esecuzione di zend_activate_modules() e prima ancora che qualsiasi script venga compilato. Un campo con un nome annidato come ZZZZ…[x]=1 fa sì che php_register_variable_ex() inserisca il nome esterno come nuova chiave dell'array, consentendo così all'autore dell'attacco di controllare direttamente la lunghezza della chiave da allocare. Impostare la lunghezza di quel nome in modo da superare memory_limit fa sì che il bailout si verifichi proprio durante l'allocazione della chiave.
Il controllo sui byte residui deriva dal determinismo dell'allocatore. Zend MM gestisce le piccole allocazioni tramite liste libere per dimensione, in rigoroso ordine LIFO (last in, first out), e non azzera mai i blocchi recuperati. Un campo precedente nella stessa richiesta, il cui valore viene copiato, decodificato e immediatamente rilasciato da PHP, può quindi essere dimensionato in modo tale che il suo contenuto rilasciato occupi esattamente il blocco che l'array corrotto richiede successivamente. La voce obsoleta si trasforma in un valore interamente controllato dall'attaccante, che abbiamo utilizzato per creare un array contraffatto il cui puntatore al distruttore rimanda alla funzione system() di libc, con la stringa del comando residente nello stesso blocco di memoria.
L'unico elemento residuo lato server di un tentativo riuscito è un errore fatale generico nel log del web server:
PHP Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 3670048 bytes) in Unknown on line 0
L'indizio rivelatore è in Unknown on line 0: conferma che nessun file PHP è mai stato in esecuzione.
CVE-2026-109619: stato globale non valido, utilizzato da una richiesta successiva
La seconda CVE segue lo stesso meccanismo, ma il danno interessa lo stato associato al processo, il che sposta la finestra di sfruttamento a una richiesta successiva. In questo caso, l'operazione interrotta memorizza lo stato in una variabile globale e dipende dal fatto che venga raggiunta la propria chiamata cleanup per cancellarlo. PHP memorizza nella cache i contesti attivi di serializzazione e deserializzazione in variabili globali del modulo e riutilizza il puntatore memorizzato nella cache ogni volta che il livello di nidificazione è diverso da zero. Solo la corrispondente chiamata destroy decrementa quel livello e azzera il puntatore, e solo la routine di inizializzazione della richiesta di un'estensione lo reimposta. La gestione delle sessioni funziona in modo simile: salva l'identificatore della sessione, lo stato dell'handler, le variabili di sessione e lo stato di avanzamento del caricamento in variabili globali che vengono cancellate tramite specifiche routine di shutdown.
Quando un errore fatale interrompe una di queste operazioni, la corrispondente procedura di teardown viene saltata. La variabile globale conserva il puntatore e il contatore di annidamento continua a indicare che l'operazione è attiva. La chiusura della richiesta rilascia quindi l'intero heap associato alla richiesta, ma la variabile globale sopravvive perché appartiene al processo, non alla richiesta. PHP si ritrova con un puntatore a livello di modulo che punta a memoria che ha già rilasciato.
Due fattori rendono questo scenario raggiungibile e controllabile:
- Riutilizzo dei worker: nei deployment con worker persistenti come Apache
mod_php, lo stesso processo gestisce la richiesta successiva. La variabile globale obsoleta viene trovata intatta, l'operazione viene considerata in corso e il puntatore rilasciato viene letto, scritto o usato per eseguire diramazioni logiche. Qualunque cosa occupi quella memoria a quel punto sarà sotto il controllo dell'autore dell'attacco, perché anche la richiesta intermedia gli appartiene: è lui a sceglierne le dimensioni e il contenuto, e il comportamento di allocazione di Zend MM è sufficientemente deterministico da collocarli in modo affidabile. PHP-FPM arresta un worker se l'avvio di una richiesta fallisce, eliminando così il riutilizzo in caso di interruzioni nella fase di avvio. Al contrario, se uno script già in esecuzione viene interrotto, il processo rimane attivo e altrettanto esposto. - Ordine di avvio: php_request_startup() analizza le variabili della richiesta prima di eseguire le funzioni di inizializzazione delle richieste delle estensioni. Pertanto, la gestione dello stato di avanzamento dei caricamenti multipart esegue il codice di sessione e di serializzazione prima che le estensioni responsabili di tali variabili globali abbiano avuto modo di ripristinarle, consentendo l'utilizzo di un puntatore obsoleto nella fase più precoce di una richiesta.
Oltre all'esaurimento della memoria, questa situazione può verificarsi quando viene superato il tempo massimo di esecuzione o se il client interrompe la connessione. In entrambi i casi viene seguito lo stesso percorso zend_bailout(). L'attacco si articola quindi in due fasi: una richiesta compromette il worker e una seconda provoca l'utilizzo della memoria rilasciata.
Impatto delle vulnerabilità
La gravità di entrambi i problemi deriva dal fatto che si verificano nelle prime fasi della gestione delle richieste. La compromissione avviene mentre PHP sta ancora preparando le variabili interne della richiesta, prima di inizializzare le estensioni e di eseguire il codice dell'applicazione. Pertanto, qualsiasi endpoint PHP accessibile su un server compromesso costituisce una superficie di attacco attiva, inclusi i moduli statici, i percorsi di accesso e i controlli dello stato di integrità. Non ci sono autenticazioni da aggirare, sessioni da rubare né endpoint vulnerabili da scoprire. L'esposizione è indipendente dal CMS, dal framework o dai plugin utilizzati.
Inoltre, l'attacco lascia pochissime tracce. I processi di lavoro rimangono stabili, il server restituisce risposte HTTP 200 e i log dell'applicazione non registrano anomalie, poiché il codice dell'applicazione non è mai stato eseguito. L'unica traccia sul server è un errore fatale dovuto al superamento del limite di memoria, registrato nel log degli errori del server web. Questo viene solitamente ignorato come rumore di fondo, privando i responsabili della difesa della telemetria web utile per identificare gli endpoint compromessi.
Nei server web multiprocesso come Apache prefork, ogni worker deriva da un unico processo principale; pertanto, la struttura di memoria su cui l'autore dell'attacco fa affidamento rimane costante anche dopo il riavvio dei processi di lavoro. Questa stabilità consente a un avversario di perfezionare i payload senza alcun costo: i meccanismi di monitoraggio considerano normale il riavvio del worker dopo un tentativo fallito.
Le due CVE presentano differenze in termini di portata e tempistica di sfruttamento che comportano conseguenze operative. La CVE-2026-109618 è autonoma: una singola richiesta corrompe i propri dati e l'effetto si verifica durante la pulizia di quella stessa richiesta. Ciò rende l'attacco rapido, atomico e difficile da ricostruire a posteriori. La CVE-2026-109619 coinvolge diverse richieste: richiede una fase di compromissione e un'altra di attivazione contro lo stesso worker, ma interessa un insieme più ampio di stati interni e può essere attivata tramite timeout e disconnessioni del client, oltre che dall'esaurimento della memoria. I deployment con worker persistenti sono quelli che presentano un'esposizione significativa a questa seconda categoria.
Lo sfruttamento riuscito di una delle due vulnerabilità consente di eseguire comandi con i privilegi dell'utente del server web, offrendo agli attaccanti un punto di accesso iniziale da cui distribuire web shell, sottrarre credenziali di accesso a database e API dai file di configurazione e spostarsi lateralmente all’interno delle reti aziendali. L'applicazione di patch è l'unica remediation completa. Quando non è possibile applicare subito le patch, il monitoraggio del comportamento degli endpoint è la principale linea di difesa, perché lo sfruttamento non è visibile a livello HTTP, mentre i suoi effetti lo sono.
La mia organizzazione è a rischio?
Entrambe le vulnerabilità sono presenti in tutte le versioni di PHP delle serie 7.x e 8.x. Lo specifico ordinamento dell'HashTable alla base della vulnerabilità CVE-2026-109618 è stato introdotto da un'ottimizzazione delle prestazioni nell'ottobre 2017 e implementato per la prima volta in PHP 7.3.0 (dicembre 2018). I percorsi di serializzazione e di sessione alla base di CVE-2026-109619 risalgono a tempi ancora più lontani, rispettivamente a PHP 7.1 e PHP 5.4. In altre parole, la finestra di vulnerabilità sfruttabile esiste nelle versioni rilasciate di PHP da oltre sette anni. Nessun ramo supportato ne è esente.
La versione da sola non determina l'esposizione. La condizione di esaurimento della memoria deve potersi verificare mentre PHP analizza i dati della richiesta; ciò richiede che il corpo della richiesta possa crescere abbastanza da esaurire il limite di memoria prima che venga eseguito qualsiasi script. In pratica, ciò significa che la configurazione deve soddisfare approssimativamente la seguente condizione:
memory_limit < 4 × post_max_size
I valori predefiniti di PHP (post_max_size = 8M, memory_limit = 128M) non soddisfano questa condizione, pertanto un'installazione standard non può essere sfruttata tramite la tecnica dei dati codificati nell'URL che abbiamo illustrato. Tuttavia, molte implementazioni in produzione sono configurate per accettare caricamenti di file di grandi dimensioni, e combinazioni comuni come post_max_size = 64M con memory_limit = 128M, oppure post_max_size = 100M con memory_limit = 256M, soddisfano effettivamente tale condizione. Si noti che post_max_size può essere configurato per directory tramite .htaccess o su un host virtuale. Pertanto, in un hosting condiviso, un singolo sito può adottare una configurazione vulnerabile senza la necessità di modificare la configurazione dell'intero server. CrowdStrike ha riprodotto il problema riducendo memory_limit a 16M e aumentando post_max_size a 64M solo per mantenere un payload di prova di piccole dimensioni; lo stesso errore si verifica con payload più grandi rispetto ai limiti più elevati indicati sopra.
Per vedere quale versione di PHP e quale configurazione sono in esecuzione sul tuo server, esegui php --version. Per visualizzare i valori impostati sul tuo server per le opzioni elencate sopra, esegui il comando:
php -i | grep -E "^(memory_limit|post_max_size|upload_max_filesize|Loaded Configuration File)”.
In che modo CrowdStrike Falcon protegge i clienti
CrowdStrike adotta un approccio a più livelli per rilevare le attività di sfruttamento e post-sfruttamento tramite machine learning e rilevamenti basati sul comportamento, inclusi gli indicatori di attacco (IOA). La piattaforma Falcon offre visibilità e copertura di rilevamento pronte all'uso per le attività successive allo sfruttamento di queste vulnerabilità di PHP. Gli IOA esistenti rilevano e prevengono le comuni attività successive allo sfruttamento, tra cui il deployment di web shell, la comparsa di processi secondari sospetti avviati da PHP o dai processi del web server e l'esecuzione di comandi dannosi.
CrowdStrike continua a studiare le tecniche di sfruttamento associate a questa vulnerabilità.

Figura 1. Catena di sfruttamento delle vulnerabilità di PHP
Individuazione delle versioni vulnerabili di PHP con Falcon Exposure Management
CrowdStrike Falcon® Exposure Management offre una visibilità completa su tutti gli ambienti interessati. Le funzionalità Network Scan (Netscan) ed External Attack Surface Management (EASM) consentono di rilevare tempestivamente, tramite banner, gli asset esposti a Internet. Inoltre, la visibilità basata su sensori consente la valutazione automatizzata delle patch a valle fornite dai produttori su tutte le distribuzioni Linux supportate, nonché sui sistemi operativi Windows e macOS.
Falcon Exposure Management rileva queste CVE in tutte le versioni supportate del sensore tramite il proprio gestore di pacchetti esistente e controlli mirati sulle applicazioni. Questi controlli non richiedono una versione minima del sensore.
CrowdStrike ha inoltre aggiunto i seguenti rilevamenti basati sul sensore. Questi rilevamenti individuano installazioni PHP che i controlli esistenti potrebbero non rilevare, come ad esempio installazioni personalizzate o effettuate al di fuori di un gestore di pacchetti. Sono disponibili solo nelle versioni di sensore elencate:
- Windows (sensore Falcon 7.40 o versioni successive): controllo a livello di sistema degli eseguibili PHP sul drive predefinito. Questo consente di individuare ulteriori installazioni di PHP che i controlli esistenti potrebbero non rilevare, comprese quelle personalizzate.
- Linux (sensore Falcon 7.31 o versioni successive): verifica dei processi in esecuzione per individuare installazioni di PHP effettuate al di fuori del gestore di pacchetti previsto.
I clienti che utilizzano versioni precedenti del sensore sono comunque coperti dal gestore di pacchetti e dai controlli mirati delle applicazioni. Non riceveranno i controlli aggiuntivi a livello di sistema e sui processi in esecuzione finché non effettueranno l'upgrade.

Figura 2. Identificazione delle versioni vulnerabili di PHP in Falcon Exposure Management
Identificazione delle minacce con Next-Gen SIEM
Falcon Next-Gen SIEM: dashboard per la risposta alle minacce emergenti
La copertura di rilevamento di CrowdStrike Falcon® Next-Gen SIEM per questa minaccia (la nuova sezione “PHP Remote Code Execution Vulnerabilities (CVE-2026-109618/109619)” della dashboard di CrowdStrike Falcon - Emerging Threat Response) è stata rilasciata in produzione nell'ambito del rilascio dei pacchetti di contenuti NG-SIEM.
La dashboard Emerging Threat Response è uno strumento di risposta mirato, progettato per offrire ai clienti CrowdStrike visibilità immediata sulle campagne attive o sulle vulnerabilità appena scoperte che potrebbero avere un impatto sul loro ambiente. Sfruttando la ricca telemetria proveniente dai moduli della piattaforma Falcon e i dati di terze parti acquisiti in Falcon Next-Gen SIEM, questa dashboard mette a disposizione query di rilevamento e threat hunting pronte all'uso, che consentono ai team di sicurezza di determinare rapidamente l'esposizione alle minacce e individuare gli asset potenzialmente compromessi.
- Vedi Next-Gen SIEM > Log management > Dashboard

Figura 3. Identificazione della dashboard Emerging Threat Response in Falcon Next-Gen SIEM
Modelli di regole di correlazione per Falcon Next-Gen SIEM
Abbiamo aggiunto un nuovo modello di regole di correlazione (CRT, Correlation Rule Template) di Falcon Next-Gen SIEM per rilevare e rispondere alle vulnerabilità CVE-2026-109618 e CVE-2026-109619. Queste regole offrono copertura contro i tentativi di sfruttamento correlati a queste vulnerabilità.
Generic - Web - PHP Memory Exhaustion During Request Startup (CVE-2026-109618 & CVE-2026-109619)
Questo rilevamento identifica errori fatali di PHP dovuti all'esaurimento della memoria che si verificano prima che uno script PHP inizi ad essere eseguito. Nello specifico, rileva un pattern rivelatore che compare nei log di errore dei server web: “Allowed memory size of N bytes exhausted” insieme a “in Unknown on line 0”. L'indicatore “Unknown on line 0” distingue gli errori della fase di avvio dai numerosi errori di memoria che si verificano all’interno degli script. In questo modo, i responsabili della difesa dispongono di un'indicazione più affidabile del fatto che qualcosa sta causando l'esaurimento della memoria da parte dell'interprete PHP prima che venga eseguito il codice dell'applicazione. Schemi come questo possono indicare un tentativo di sfruttare il motore PHP. I clienti possono visualizzare il modello qui ([US-1] [US-2] [US-3] [EU-1] [US-GOV-1] [US-GOV-2]).
Conclusioni e raccomandazioni
CrowdStrike sta monitorando attivamente questa situazione in evoluzione e fornirà aggiornamenti non appena saranno disponibili nuove informazioni. Parallelamente, continuiamo a sviluppare e distribuire nuove logiche comportamentali nella piattaforma Falcon per rilevare e prevenire attività malevole correlate alle vulnerabilità PHP divulgate CVE-2026-109618 e CVE-2026-109619.
Consigliamo ai clienti di individuare i sistemi che eseguono versioni vulnerabili di PHP e di applicare gli aggiornamenti di sicurezza più recenti in base alle indicazioni ufficiali di PHP o del fornitore.