Ruby on Rails ha pubblicato l'advisory GHSA-xr9x-r78c-5hrm il 29 luglio 2026, documentando CVE-2026-66066: una vulnerabilità critica in Active Storage che trasforma il normale processing di immagini in esecuzione remota di codice non autenticata. La catena di attacco, battezzata KindaRails2Shell dai ricercatori di Rapid7, è completamente riprodotta e automatizzata in un modulo Metasploit ufficiale. La combinazione di un default insicuro — libvips come processore d'immagine dal Rails 7.0 — e di una collisione tra due parser che leggono lo stesso file in modo incompatibile rende la superficie d'attacco eccezionalmente ampia.
- CVE-2026-66066 consente file read arbitrario non autenticato e RCE completo in applicazioni Rails con Active Storage e libvips configurato come processore d'immagine.
- La chain sfrutta una doppia falla di trust: Rails si fida del
content_typenel database, libvips si fida del proprio sniffer sui primi 10 byte, libmatio legge invece i byte 124-125 e apre un file HDF5 esterno. - Rapid7 ha validato l'exploit su Rails 6.0.6.1, 6.1.7.10, 7.2.3.1, 8.0.5 e 8.1.3; le versioni patchate sono 7.2.3.2, 8.0.5.1 e 8.1.3.1.
- Il modulo Metasploit
exploit/multi/http/rails_activestorage_vips_rceimplementa la chain completa file-read-to-RCE, con recupero automatico diSECRET_KEY_BASEda/proc/self/environ.
Come si rompe il confine: dal content_type al file arbitrario
L'attacco inizia sull'endpoint di direct upload di Active Storage. Il controller ActiveStorage::DirectUploadsController#create accetta un parametro content_type fornito dal client senza verificare i byte effettivi del file. Il blob viene creato nel database con un tipo MIME arbitrario, e questa decisione — non i dati reali — governa tutto ciò che segue.
Quando un secondo endpoint richiede una variante dell'immagine, Rails verifica signed_blob_id e variation_key separatamente, senza legare il secondo valore al primo. Un attaccante può quindi replicare un variation_key legittimo su un blob diverso, con content_type falsificato. È qui che scatta la prima rottura del confine di fiducia: Rails decide che un file è un'immagine da una riga di database, non dal suo contenuto.
La seconda rottura avviene nel processore. Libvips seleziona il loader matload quando rileva il prefisso MATLAB 5.0 nei primi dieci byte. Ma il controllo è superficiale: vips__mat_ismat usa solo vips_isprefix('MATLAB 5.0', buf) e non rileva che il file è in realtà un MAT 7.3, formato basato su HDF5. Libmatio, chiamato da libvips, legge invece i byte 124-125 dove trova MAT_FT_MAT73 = 0x0200 e apre il file come HDF5. Questa dissonanza — due parser che guardano parti diverse dello stesso header e traggono conclusioni opposte — è il cuore tecnico della vulnerabilità.
"The published chain contains two separate trust failures. Rails decides that a blob is an image from a database value, while libvips decides what parser to use from the bytes on disk." — Rapid7
Da pixel a shell: la catena RCE in tre mosse
Il file HDF5 così aperto può contenere un External File List: un riferimento a un altro file sul filesystem, il cui contenuto viene restituito come se fossero pixel. Questo permette la lettura arbitraria di qualsiasi file leggibile dall'utente del processo Rails. La cosa significativa, come nota Rapid7, è che questa fase "non richiede secret_key_base né token forgiati": è un file read puro, non autenticato, raggiungibile con una richiesta diretta.
La terza mossa scala il file read a RCE completo. Il modulo Metasploit punta direttamente a /proc/self/environ per estrarre la variabile SECRET_KEY_BASE, con un limite di lettura di 65536 byte. Con questa chiave, l'attaccante forges messaggi firmati con il serializer JSON di Active Support — che Rails usa di default — e costruisce una variation maliziosa contenente array e hash che, attraverso la catena di metodi di ImageProcessing, raggiungono Kernel#spawn o Kernel#eval.
Rapid7 ha documentato questo percorso con il serializer :json, dimostrando che tipi semplici — Hash, Array, String — sono sufficienti per l'escalation, senza bisogno di YAML o Marshal. Il modulo Metasploit, sviluppato da jburgess-r7 e mergeato nel framework ufficiale, implementa "la concrete representation-based chain documentata dal repository forense di Rails". I log di validazione mostrano il recupero della chiave seguita da una shell con uid rails.
Il default che amplifica tutto: perché Vips cambia la partita
Rails 7.0 ha reso libvips il processore d'immagine predefinito per Active Storage, sostituendo ImageMagick/MiniMagick. Questa scelta, tecnicamente motivata da prestazioni e memoria, ha creato una superficie d'attacco vastissima: ogni applicazione Rails 7+ con upload di immagini è potenzialmente esposta senza che gli sviluppatori abbiano attivamente configurato nulla. Rails 6.x è affetto solo se Vips è stato esplicitamente abilitato, una configurazione meno comune ma non rara in deployment ottimizzati.
La dimensione del problema è sottolineata dal punteggio CVSSv4 di 9.5 assegnato da Rapid7, con classificazione CWE-1188: Initialization of Resource with Insecure Default. La scelta di default — libvips senza restrizioni sulle operazioni trust — è ciò che trasforma una vulnerabilità di parsing in una crisi di sicurezza sistemica.
Il fix Rails, pubblicato nelle versioni 7.2.3.2, 8.0.5.1 e 8.1.3.1, disabilita le operazioni untrusted nell'inizializzazione di Active Storage. Ma c'è un vincolo stretto: richiede libvips >= 8.13. Sistemi con versioni precedenti di libvips non ricevono protezione dal solo aggiornamento di Rails; il boot fallisce se il requisito non è soddisfatto. Questo crea una dipendenza critica tra due componenti con cicli di rilascio indipendenti.
Cosa fare adesso
- Patch Active Storage immediatamente alle versioni 7.2.3.2, 8.0.5.1 o 8.1.3.1, verificando compatibilità con libvips >= 8.13 sul sistema target prima del deploy.
- Applicare il workaround temporaneo impostando la variabile d'ambiente
VIPS_BLOCK_UNTRUSTEDo chiamandoVips.block_untrusted(true)con ruby-vips >= 2.2.1, se il patching non è immediatamente fattibile. - Ruotare tutti i secret Rails, in particolare
SECRET_KEY_BASE: la fase di file read può aver esposto questi valori anche senza RCE confermata, e la firma di messaggi compromessi permette azioni persistenti. - Verificare la configurazione processore: se libvips è attivo in produzione, la priorità è massima; con ImageMagick/MiniMagick questo vettore specifico non è applicabile, ma la verifica deve essere esplicita.
Il precedente incompleto e il rischio di regressioni
La pull request del fix Rails rivela che CVE-2025-24293 — una vulnerabilità precedente nella stessa area — era stata risolta in modo parziale. Il patch proteggeva solo il transformer ImageMagick, non quello Vips. Questo lascia un'impronta chiara: la complessità di Active Storage, con i suoi due processori paralleli e le loro diverse superfici di parsing, ha reso i controlli di sicurezza frammentari. La classe CWE-1188, insicurezza del default, è qui letterale: il sistema si avvia con una configurazione che non distingue tra operazioni trusted e untrusted nel processore d'immagine.
Il modulo Metasploit, con i suoi due layout di esfiltrazione — 256x256 raw (32512 byte per richiesta) per Rails 7/8 e 100x100 sharpened (4900 byte) per Rails 6.x — dimostra che la chain è robusta e adattata a multiple versioni. La presenza di questo strumento pubblico, testato live su cinque versioni Rails, abbassa drasticamente la barriera all'entrata per attacchi replicabili.
Lettura: quando due parser onesti costruiscono un'arma
Il caso KindaRails2Shell non è una vulnerabilità di buffer overflow né di logica di autorizzazione difettosa. È qualcosa di più sottile e più preoccupante: due componenti che, presi singolarmente, si comportano come documentato, ma la loro composizione crea un canale di attacco. Rails si fida del database, libvips si fida del proprio sniffer, libmatio si fida del proprio parser HDF5. Nessuno di questi è un bug nel senso stretto. La loro sovrapposizione lo è.
Questo pattern — la collisione di parser con modelli di trust diversi — non è nuovo, ma la sua espressione in una pipeline di upload immagini così diffusa lo rende particolarmente pericoloso. Il fatto che la chain sia passata attraverso una fix precedente senza essere intercettata suggerisce che i controlli di sicurezza su componenti compositi richiedono una verifica esplicita degli stati di confine, non solo del comportamento individuale.
La disclosure, anticipata al 29 luglio rispetto alla data originale del 28 agosto, ha accelerato la disponibilità di strumenti di attacco. Le squadre di sicurezza devono ora operare con l'assunto che la proof-of-concept sia in circolazione attiva, anche se Rapid7 non aveva evidenza di exploitation in the wild al 30 luglio. Il tempo tra advisory e modulo Metasploit è stato di pochi giorni: la finestra di reazione è stretta, e la dipendenza da libvips >= 8.13 aggiunge un ostacolo operativo non trascurabile.
Fonti
- https://www.rapid7.com/blog/post/ra-kindarails2shell-technical-analysis-cve-2026-66066
- https://github.com/rails/rails/security/advisories/GHSA-xr9x-r78c-5hrm
- https://github.com/rails/rails/pull/56995
- https://github.com/rapid7/metasploit-framework/pull/21733
- https://www.rapid7.com/db/vulnerabilities/cve-2026-66066/
- https://www.rapid7.com/blog/post/etr-kindarails2shell-cve-2026-66066-critical-arbitrary-file-read-and-possible-remote-code-execution-in-ruby-on-rails/
Le informazioni sono state verificate sulle fonti citate e aggiornate al momento della pubblicazione.