Un bug critico di PhpSpreadsheet ha aggirato la sua stessa patch

Un bug critico di PhpSpreadsheet ha aggirato la sua stessa patch

Qualche settimana fa il team di PHPOffice ha rilasciato una correzione per un bug di deserializzazione phar in PhpSpreadsheet, tracciato come CVE-2026-34084. I ricercatori hanno poi trovato un modo per aggirare quella correzione, e il bypass è ora una vulnerabilità a sé: CVE-2026-45034, classificata 9,2 critica su CVSS v4. L'advisory, GHSA-87m4-826x-3crx, è diventato pubblico il 7 giugno. Se PhpSpreadsheet legge file di fogli di calcolo in qualsiasi punto del vostro stack, vale la pena dedicarci dieci minuti oggi.

La patch originale ha aggiunto un helper chiamato File::prohibitWrappers che avrebbe dovuto rifiutare i wrapper di stream come phar://. Lo faceva chiamando parse_url($filename, PHP_URL_SCHEME) e lanciando un'eccezione se lo schema restituiva una stringa più lunga di un carattere. Il problema è che questo controllo non è equivalente alla domanda «questo percorso usa un wrapper». Se si passa un percorso della forma phar:///path/file.phar/inner, con tre slash dopo lo schema, parse_url restituisce il booleano false invece della stringa dello schema. Il controllo viene saltato, l'helper ritorna senza errori, e IOFactory::load() raggiunge comunque il wrapper phar.

Perché la versione PHP è più importante del punteggio CVSS

Ciò che accade dopo dipende interamente dalla versione di PHP in uso, e questa è la parte che i team fraintendono. Su PHP 7.x, è sufficiente toccare il wrapper phar tramite is_file perché PHP deserializzi i metadati del phar, attivando __wakeup e __destruct su oggetti controllati dall'attaccante. Questo equivale a un'esecuzione remota di codice completa. I ricercatori l'hanno dimostrato sul branch 1.x con PHP 7.4 usando una gadget chain che scriveva un file marcatore. Su PHP 8.x, la deserializzazione automatica dei metadati per le operazioni sui file ordinarie è stata rimossa, quindi a livello di PhpSpreadsheet il bug si riduce a una primitiva di lettura file phar. L'esecuzione di codice ritorna solo se qualcosa a valle nell'applicazione chiama Phar::getMetadata su quel percorso.

Quindi il ragionamento è questo. Siete ancora su PHP 7.x? Questo è critico e immediato. Su PHP 8.x non siete automaticamente al sicuro: siete a una sola chiamata a Phar::getMetadata dallo stesso risultato, e una lettura arbitraria di file phar non è una vulnerabilità che volete avere in un endpoint di importazione documenti.

Cosa ci dice questo su come le patch vengono aggirate

Durante i nostri engagement di penetration testing, la deserializzazione phar è una delle prime cose a cui ricorriamo quando un'applicazione accetta un percorso file, un nome file o un upload e lo passa a una libreria. La parte interessante di questo CVE non è il trucco del wrapper, ma la forma della correzione originale. La patch validava lo schema che parse_url restituiva, quando la regola corretta era «questo percorso non deve fare riferimento a nessun wrapper». Correggere il sintomo visibile lascia aperti i casi non testati. Tre slash sono stati sufficienti.

Osserviamo lo stesso riflesso al di fuori di PHP. Dal nostro lavoro con PHP e Docker nella costruzione di app cloud-native per clienti enterprise, le librerie che analizzano input non attendibili sono quelle che meritano una seconda occhiata a ogni aggiornamento delle dipendenze, non solo quando arriva un CVE. Nei nostri progetti nativi per iOS e Android il pattern si ripete con SDK di terze parti che accettano silenziosamente un URL o un percorso. Il team si fida della libreria, la libreria si fida dell'input, e nessuno è responsabile del confine tra i due. Una nuova patch non mette fine a una classe di vulnerabilità, è un ulteriore punto dati su dove si trova effettivamente quel confine.

Cosa fare questa settimana

Tre passi concreti, in ordine:

  1. Aggiornare PhpSpreadsheet. La correzione è nella versione 1.30.5 del branch 1.x, o nella release patchata del branch che seguite: 2.1.17, 2.4.6, 3.10.6, o 5.8.0. Il nuovo codice abbandona il trucco di parse_url e usa pattern espliciti per individuare phar:// e wrapper simili.
  2. Cercate nel codebase IOFactory::load e Reader::load, poi tracciate ogni chiamata fino all'origine del percorso. Qualsiasi chiamata raggiungibile da un nome file fornito dall'utente, un nome di upload o un URL è quella che conta. Bloccate la versione patchata prima che quell'endpoint vada di nuovo in produzione.
  3. Aggiungete difesa in profondità a livello PHP. Disabilitate i wrapper di stream phar://, php://, data:// e expect:// dove il codice di importazione è in esecuzione, e validate o canonicalizzate ogni percorso prima che raggiunga un loader. Se siete ancora su PHP 7.x, questo CVE è un ulteriore motivo per completare quella migrazione. Il runtime ha rimosso il sink di deserializzazione automatica in PHP 8.

Se sviluppate in PHP, un'abitudine utile è trattare ogni libreria di gestione file come ostile ai percorsi non attendibili per impostazione predefinita, come facciamo noi quando costruiamo e revisioniamo servizi backend per clienti con requisiti di conformità reali. Il costo di quest'abitudine è qualche minuto per dipendenza. Il costo di saltarla è un RCE nella pipeline dei documenti.

Se una libreria di terze parti che silenziosamente cerca phar:// nel vostro stack PHP vi sembra un problema che preferireste individuare prima di un attaccante, parliamone.

expert-analysisphpsecuritytech-newsvulnerability