Mongoose è una libreria Object Data Modeling (ODM) per MongoDB che semplifica le interazioni con i database nelle applicazioni Node.js. Fornendo una soluzione basata su schemi, Mongoose consente di mappare gli oggetti JavaScript in documenti MongoDB, agendo come un livello di astrazione che aiuta a strutturare i dati per una più facile gestione e validazione. Grazie a funzioni come il middleware per l'esecuzione di logica personalizzata e un sistema intuitivo di creazione di query, Mongoose migliora l'efficienza del lavoro con MongoDB. Mongoose, descritto come "elegante modellazione di oggetti MongoDB per Node.js", ha ottenuto 27.000 stelle su GitHub, a testimonianza del suo ampio utilizzo e apprezzamento da parte degli sviluppatori.
Programma di borse di studio OPSWAT e scoperta di vulnerabilità critiche
Il programmaOPSWAT Critical Infrastructure Cybersecurity Graduate Fellowship Program, con sede in Vietnam, offre agli studenti laureati un'esperienza pratica nella sicurezza delle infrastrutture critiche. Nell'ambito di questo programma, i borsisti hanno l'opportunità di analizzare e affrontare le vulnerabilità della sicurezza informatica, collaborando con gli esperti OPSWAT per affrontare le sfide del mondo reale in aree come il rilevamento di malware, la sicurezza dei file e la prevenzione delle minacce.
Durante il programma OPSWAT Fellowship, i partecipanti indagano e riproducono sistematicamente le CVE conosciute su vari prodotti, librerie e sistemi operativi. Nell'ambito di questa iniziativa, Dat Phung - uno dei nostri illustri borsisti - ha scelto di esaminare Mongoose a causa della sua diffusa adozione in ambienti di produzione. Nel novembre 2024, durante un'analisi approfondita della libreria, ha scoperto una vulnerabilità critica in Mongoose. La vulnerabilità consentiva a un aggressore di sfruttare il valore $where, portando potenzialmente all'esecuzione di codice remoto (RCE) sul server dell'applicazione Node.js. Dopo aver prontamente segnalato il problema a Mongoose, è stata rilasciata una patch come parte della versione 8.8.3 e la CVE-2024-53900 è stata resa nota nel National Vulnerability Database (NVD).
CVE-2024-53900 e CVE-2025-23061 Cronologia
- 7 novembre 2024: Dat Phung ha identificato una vulnerabilità critica in Mongoose e ha inviato un rapporto di sicurezza a Snyk.
- 26 novembre 2024: Mongoose ha rilasciato la versione 8.8.3 per risolvere questa vulnerabilità.
- 2 dicembre 2024: Il National Vulnerability Database (NVD) ha reso nota la CVE-2024-53900 per questa vulnerabilità.
- 17 dicembre 2024: Analizzando la patch 8.8.3 di Mongoose, Dat Phung ha trovato un bypass che permetteva ancora di eseguire RCE (Remote Code Execution). Un rapporto di sicurezza dettagliato è stato inviato a Tidelift.
- 13 gennaio 2025: Mongoose rilascia la versione 8.9.5, introducendo una patch migliorata che risolve efficacemente il bypass.
- 15 gennaio 2025: Il National Vulnerability Database (NVD) ha divulgato ufficialmente CVE-2025-23061, sottolineando la gravità della vulnerabilità appena identificata.
Il metodo Populate() di Mongoose
Mongoose fornisce anche un'utile funzione chiamata populate() che migliora la capacità di lavorare con le relazioni tra documenti. Mentre le versioni di MongoDB ≥ 3.2 hanno l'operatore di aggregazione $lookup per i join, populate() di Mongoose offre un'alternativa più potente per sostituire automaticamente un riferimento con i dati corrispondenti di documenti correlati. Ciò è particolarmente utile per gestire le relazioni tra diverse raccolte di MongoDB, come quando un documento fa riferimento a un altro tramite il suo _id. [2]
Quando si definisce uno schema in Mongoose, un campo può essere impostato per fare riferimento a un altro modello usando l'opzione ref. Il metodo populate() viene quindi utilizzato per sostituire il campo di riferimento (un ObjectId) con il documento completo del modello correlato. Ad esempio, in un'applicazione di libreria, il campo autore nel bookSchema fa riferimento al documento Autore e il campo recensione fa riferimento al documento Recensioni. Il metodo populate() consente agli sviluppatori di sostituire il campo autore (che è un ObjectId) con l'intero documento Author quando si interroga il modello del libro.
Il populate() consente agli sviluppatori di sostituire il campo autore (che è un ObjectId) con il documento autore completo quando si interroga il modello del libro:
Inoltre, il metodo populate() di Mongoose supporta query personalizzate per definire quali documenti correlati vengono recuperati e come vengono recuperati. Proprietà come match e options consentono agli sviluppatori di filtrare, ordinare, limitare e saltare i documenti correlati, offrendo funzionalità flessibili di recupero dei dati.
Analisi CVE-2024-53900
Nell'ambito del programma OPSWAT Cybersecurity Graduate Fellowship, mentre analizzava Mongoose per riprodurre le CVE conosciute, Dat Phung ha condotto una revisione completa del funzionamento interno del metodo populate(), che svolge un ruolo chiave nella gestione delle relazioni tra i documenti MongoDB. Il metodo populate() supporta argomenti sia di tipo stringa che di tipo oggetto e gli sviluppatori possono utilizzare l'opzione match per applicare filtri specifici ai dati recuperati:
Nell'esempio precedente, l'opzione match è un oggetto filtro che può includere gli operatori di query di MongoDB, come illustrato nel manuale Query and Projection Operators - MongoDB Manual v8.0. Un operatore degno di nota è $where, che consente l'esecuzione di JavaScript direttamente sul server MongoDB. Tuttavia, l'esecuzione sul server MongoDB è limitata e supporta solo operazioni e funzioni di base.
Dat Phung ha condotto un'analisi approfondita del codice sorgente di Mongoose per comprendere il flusso di lavoro del metodo populate(). Ha determinato che dopo che l'applicazione chiama il metodo populate() sul modello, viene attivata la funzione populate(). All'interno di questa funzione, Mongoose chiama la funzione _execPopulateQuery() , che esegue la query con l'operatore $where sul server MongoDB. Successivamente, tutti i documenti della collezione straniera vengono recuperati per essere popolati nei passi successivi.
Dopo aver recuperato i dati da MongoDB, Mongoose esegue la funzione di callback _done(), che chiama _assign() per preparare i dati prima di "unire" i due modelli chiamando la funzione assignVals().
La vulnerabilità può verificarsi quando i dati recuperati vengono elaborati dalla funzione assignVals() di Mongoose. Questa funzione controlla se l'opzione match è un array e, in caso affermativo, passa ogni operatore alla funzione sift(). La funzione sift() , importata da una libreria esterna con lo stesso nome, elabora queste query localmente sul server dell'applicazione. Questa elaborazione locale presenta un rischio per la sicurezza, soprattutto quando si tratta di input controllati dall'utente.
Per approfondire questo aspetto, Dat Phung ha modificato i valori dell'opzione match per garantire che le condizioni fossero soddisfatte, invocando così la funzione sift() per un'ulteriore analisi del flusso di dati.
Con la condizione in atto, l'operatore $where è stato successivamente passato alla funzione sift().
La libreria sift è una leggera utility JavaScript progettata per filtrare e interrogare collezioni di dati come array o oggetti JSON utilizzando una sintassi simile a quella di MongoDB. Secondo la documentazione ufficiale, "Sift è una piccola libreria per utilizzare le query MongoDB in JavaScript". La funzione sift() valuta le operazioni di filtro simili a MongoDB sul server dell'applicazione invece che sul server del database, il che può esporre il sistema a rischi significativi per la sicurezza quando si elaborano input non attendibili.
Continuando la sua analisi, il nostro collega ha identificato un problema nella funzione createDefaultQueryTester() della libreria sift. Questa funzione converte ogni operazione dell'array match in funzioni JavaScript eseguibili, che vengono poi utilizzate per filtrare ed elaborare localmente i dati dei documenti MongoDB. A tale scopo, createDefaultQueryTester() richiama la funzione createNamedOperation(), passando come argomenti operazioni come $where dall'array di corrispondenze.
Per ogni operazione nell'array match, createNamedOperation verifica se l'operazione è supportata e la passa alla funzione corrispondente.
Se l'operazione è $where, viene generata una funzione JavaScript usando il valore grezzo "params", che deriva dall'operatore $where nell'array match e può essere controllato dall'utente.
CVE-2024-53900: Dettagli dell'exploit
Mentre MongoDB limita l'esecuzione di funzioni JavaScript tramite l'operazione $where, come analizzato in precedenza, la funzione sift() consente l'esecuzione di tali funzioni senza tali restrizioni. Questa mancanza di validazione e restrizione dell'input introduce una significativa vulnerabilità di sicurezza, in quanto il valore "params", controllato direttamente dall'input dell'utente, può essere sfruttato, portando potenzialmente ad attacchi di iniezione di codice. Per esaminare più a fondo questo problema, Dat Phung ha costruito la seguente query:
Inizialmente, la query non è riuscita a eseguire un altro processo, generando il seguente errore:
Questo errore indica che Mongoose tenta di eseguire l'operazione $where sul server MongoDB prima di passare il controllo alla funzione sift(). Tuttavia, a causa delle restrizioni sulle funzioni JavaScript nella clausola $where di MongoDB, si verifica un errore che impedisce l'esecuzione della query. Di conseguenza, Mongoose arresta il processo prima di raggiungere la funzione sift() .
Per aggirare la limitazione, il nostro collega ha sfruttato la variabile "global" presente sull'application server, che non esiste sul server MongoDB. Questo approccio gli ha permesso di aggirare la restrizione sul server MongoDB e di consentire alla query di raggiungere la funzione sift():
Con questo valore, quando Mongoose esegue l'operazione $where su MongoDB, l'assenza della variabile "global" fa sì che l'operatore ternario (typeof global != "undefined" ?global.process.mainModule.constructor._load("child_process").exec("calc") : 1) per restituire 1, evitando che MongoDB lanci un errore. Di conseguenza, la query viene eseguita sul server MongoDB senza problemi.
Tuttavia, quando lo stesso valore raggiunge la funzione sift(), che viene eseguita sul server dell'applicazione in cui è disponibile la variabile "globale", si attiva la creazione della funzione seguente:
Prova di concetto dell'esecuzione di codice remoto (RCE)
Nell'esempio di applicazione fornito all'inizio del blog, se un aggressore inviasse la seguente richiesta, potrebbe eseguire con successo un attacco di tipo Remote Code Execution (RCE):
Il video mostra il Proof of Concept per la CVE-2024-53900 che interessa le versioni di Mongoose precedenti alla 8.8.3, che mancano di un'adeguata convalida dell'input per prevenire l'uso improprio dell'operatore $where insieme alla libreria sift.
Correzione incompleta e CVE-2025-23061
Sulla base del rapporto di sicurezza di Dat Phung, Mongoose ha introdotto una patch per risolvere la vulnerabilità precedentemente identificata (CVE-2024-53900) prima della sua divulgazione pubblica. La patch in questione(Automattic/mongoose@33679bc) ha aggiunto un controllo per impedire l'uso di $where all'interno della proprietà match passata a populate().
Questo frammento controlla se la proprietà match passata in populate() è un array. Se lo è, il codice itera ogni oggetto dell'array per vedere se contiene l'operatore $where. Se viene rilevato $where, viene sollevato un errore, impedendo al payload dannoso di propagarsi alla rischiosa funzione sift().
Di conseguenza, il payload che sfrutta la CVE-2024-53900 non supera questo controllo perché un oggetto nell'array match contiene $where, impedendogli di fatto di raggiungere sift().
Mentre questo aggiornamento blocca correttamente l'uso diretto di $where all'interno di un singolo livello di annidamento, non riesce a rilevare $where quando è incorporato all'interno di un operatore $or, una struttura che sia MongoDB che la libreria sift supportano pienamente.
Di conseguenza, un aggressore può annidare $where sotto $o per eludere il controllo a livello singolo della patch. Poiché Mongoose ispeziona solo le proprietà di primo livello di ogni oggetto nell'array di corrispondenza, il payload di aggiramento non viene rilevato e alla fine raggiunge la libreria sift, consentendo l'RCE dannoso.
Prova di concetto per CVE-2025-23061
Per illustrare la natura incompleta della correzione, Dat Phung ha ricostruito l'applicazione di esempio utilizzando una versione di Mongoose 8.9.4 (successiva alla 8.8.3). Annidando $whereall'interno di una clausola $or, un utente malintenzionato può aggirare il controllo e ottenere un RCE.
L'exploit proof-of-concept dimostra come la CVE-2025-23061 possa essere attivata nelle versioni di Mongoose precedenti alla 8.9.5, consentendo a un utente malintenzionato di eseguire codice arbitrario sul server:
Mitigazione e guida
Per ridurre le vulnerabilità discusse sopra, assicuratevi che il vostro sistema sia aggiornato all'ultima versione di Mongoose.
MetaDefender Core che utilizza il motore SBOM è in grado di rilevare questa vulnerabilità
OPSWAT MetaDefender Core, dotato di funzionalità avanzate di SBOMSoftware Bill of Materials), consente alle aziende di adottare un approccio proattivo nell'affrontare i rischi per la sicurezza. Esaminando le applicazioni software e le loro dipendenze, MetaDefender Core identifica le vulnerabilità note, come CVE-2024-53900 e CVE-2025-23061, all'interno dei componenti elencati. In questo modo i team di sviluppo e di sicurezza sono in grado di dare priorità alle attività di patch, mitigando i potenziali rischi per la sicurezza prima che possano essere sfruttati da attori malintenzionati.
Di seguito è riportata una schermata di CVE-2024-53900 e CVE-2025-23061, rilevate da MetaDefender Core con SBOM:
Inoltre, i CVE possono essere rilevati anche da MetaDefender Software Supply Chainche sfrutta MetaDefender Core con SBOM per identificare queste vulnerabilità.