Il maggio 2025 ha segnato la divulgazione pubblica di una vulnerabilità di sicurezza critica, CVE-2025-1974, soprannominata IngressNightmare, che colpisce il controller ingress-nginx di Kubernetes, ampiamente distribuito nelle infrastrutture cloud native. Questa vulnerabilità consente agli aggressori non autenticati di iniettare configurazioni arbitrarie in NGINX, portando potenzialmente a RCE (esecuzione di codice remoto) non autorizzati e alla compromissione completa del cluster.
Nell'ambito del programma OPSWAT Fellowship, i nostri borsisti hanno condotto un'analisi tecnica approfondita per comprendere meglio le cause, il percorso di sfruttamento e le strategie di mitigazione di questo problema ad alta gravità.
Panoramica di CVE-2025-1974
CVE-2025-1974 è una vulnerabilità critica di template injection identificata nelle versioni di ingress-nginx fino alla 1.11.4 e in particolare nella 1.12.0. Gli aggressori con accesso a livello di nodo a un cluster Kubernetes possono sfruttare questa falla per eseguire codice arbitrario utilizzando RCE attraverso il controller ingress-nginx che, per impostazione predefinita, ha privilegi estesi, compreso l'accesso a segreti critici all'interno del cluster.
Il Kubernetes Security Response Committee ha assegnato a questa vulnerabilità un punteggio CVSS v3.1 di 9.8 (gravità critica):
Componenti chiave di questa analisi
Panoramica di Kubernetes
Kubernetes (K8s) è una piattaforma open-source per automatizzare la distribuzione, la scalabilità e la gestione operativa delle applicazioni containerizzate. I cluster Kubernetes sono tipicamente composti da più macchine, che possono includere sia hardware fisico che macchine virtuali, che lavorano collettivamente per fornire ambienti applicativi altamente disponibili, scalabili e gestibili.
Controllore d'ingresso NGINX
NGINX ingress controller (ingress-nginx) è un ingress controller open-source costruito sopra il server web NGINX. Opera all'interno di un cluster Kubernetes, funzionando principalmente come reverse proxy e bilanciatore di carico. Questo controller interpreta le risorse Ingress definite dagli utenti e le traduce in configurazioni NGINX attivabili per instradare il flusso di traffico all'interno del cluster.
L'esame di ammissione e il suo ruolo
Ingress-nginx si integra con Kubernetes utilizzando un servizio webhook chiamato AdmissionReview. Questo servizio è fondamentale per elaborare gli oggetti nativi di Kubernetes Ingress e tradurli in configurazioni NGINX convalidate e sintatticamente corrette. Sebbene AdmissionReview garantisca l'accuratezza della configurazione, opera in modo indipendente dal controller ingress-nginx e in genere manca di controlli di autenticazione rigorosi. Questa mancanza di autenticazione rigorosa è un fattore chiave che ha contribuito all'exploit di CVE-2025-1974.
Sfruttamento delle vulnerabilità e analisi tecnica
Meccanismo di sfruttamento
Lo sfruttamento di CVE-2025-1974 inizia con una richiesta dannosa. Gli aggressori creano una richiesta dannosa al webhook AdmissionReview, costringendo NGINX a caricare dinamicamente una libreria condivisa in fase di esecuzione. Sulla base di questo meccanismo, i nostri collaboratori hanno analizzato sia il webhook di AdmissionReview che il flusso di lavoro di NGINX per comprendere questo percorso di exploit.
Vulnerabilità dell'iniezione di template
Su AdmissionReview webhook, durante l'elaborazione delle richieste in arrivo, la funzione CheckIngress trasforma gli oggetti Kubernetes Ingress in file di configurazione NGINX validi. Il flusso procede come segue:
- Ogni configurazione viene analizzata e passata a generateTemplate per essere formattata secondo i modelli NGINX predefiniti.
- Successivamente, testTemplate convalida la configurazione generata con il binario NGINX sottostante.
Tutte le configurazioni di NGINX si basano su modelli predefiniti che si trovano nel file nginx.tmpl all'interno del codice sorgente di ingress-nginx:
All'interno della funzione generateTemplate, la configurazione viene elaborata come mostrato nel seguente frammento di codice:
Tuttavia, la validazione e la sanitizzazione dell'input sono insufficienti. In particolare, il campo uid di un oggetto Ingress viene inserito direttamente nel modello di configurazione di NGINX, creando un punto di iniezione. Un utente malintenzionato può sfruttare questa situazione fornendo un input di tipo artigianale, come uid="1234#;\n}\n}}n}n injection_value".
Questo input dannoso consente l'iniezione dell'ambito globale nel modello NGINX, consentendo agli aggressori di attivare direttive NGINX arbitrarie e di ottenere potenzialmente un RCE.
Dall'iniezione di modelli all'esecuzione di codice remoto
Spiegazione della funzione testTemplate()
Dopo che la configurazione di NGINX è stata generata dalla funzione generateTemplate, la funzione testTemplate crea un file di configurazione temporaneo ed esegue la libreria NGINX con il comando nginx -c {config_file} -t. In questo modo si obbliga il binario NGINX ad analizzare e convalidare la configurazione.
Per sfruttare la vulnerabilità, un aggressore deve identificare una direttiva in grado di eseguire codice dannoso. Inizialmente, i nostri collaboratori hanno identificato la direttiva load_module come potenzialmente utile, poiché questa direttiva consente a NGINX di caricare plugin esterni. Tuttavia, questa direttiva è consentita solo nella fase iniziale dell'analisi della configurazione, il che non corrisponde al nostro punto di iniezione.
Per risolvere questa sfida, abbiamo proseguito le ricerche, che hanno portato alla direttiva ssl_engine, descritta come "Il modulo può essere caricato dinamicamente da OpenSSL durante i test di configurazione". Questo ha suscitato curiosità per la sua capacità di caricare dinamicamente i moduli, rendendo necessario un esame più approfondito.
Capire la direttiva ssl_engine
Per capire meglio come NGINX gestisce la direttiva ssl_engine e per determinare le condizioni in cui NGINX permette il caricamento dinamico di moduli aggiuntivi tramite questa direttiva, abbiamo esaminato il codice sorgente di NGINX.
All'avvio, NGINX carica il suo stato iniziale, quindi analizza i file di configurazione riga per riga. Ogni direttiva è gestita dalla struttura nginx_command_t, con la direttiva ssl_engine che invoca direttamente i ngx_openssl_commands.
Analizzando la funzione ngx_openssl_commands, i nostri collaboratori hanno scoperto che si basa sul supporto di OpenSSL, in particolare sulla funzione ENGINE_by_id, utilizzata per i moduli SSL con accelerazione hardware.
Analizzando la funzione ENGINE_by_id, abbiamo scoperto che consente il caricamento dinamico delle librerie condivise. Inoltre, se la libreria è compilata con l'estensione __attribute__((constructor)), la funzione associata può essere eseguita immediatamente dopo il caricamento. Ciò indica che sfruttando la direttiva ssl_engine, un aggressore potrebbe caricare librerie condivise arbitrarie sull'host, portando potenzialmente a un RCE.
Obiettivi delle librerie condivise e strategia di attacco
Per facilitare in modo affidabile l'esecuzione del codice, il passo successivo consiste nell'individuare una libreria condivisa. Piuttosto che affidarsi a librerie esterne, un approccio più valido e controllato emerge dal comportamento stesso di NGINX: il meccanismo del body buffer del client. Questa funzione consente a NGINX di scaricare le richieste in entrata di grandi dimensioni in file temporanei, aprendo opportunità di sfruttamento basate su un comportamento prevedibile di gestione dei file.
Per impostazione predefinita, quando una richiesta in arrivo supera gli 8KB, NGINX scrive il corpo della richiesta in un file temporaneo situato in /tmp/nginx/client-body, utilizzando un nome di file nel formato cfg-{random_value}. Questi file temporanei vengono conservati per un massimo di 60 secondi tra le parti di un messaggio ricevuto.
Dopo aver scritto un corpo parziale della richiesta in un file temporaneo, NGINX rinvia la cancellazione fino a quando non viene ricevuto l'intero corpo. Se la richiesta rimane incompleta e non vengono ricevuti dati per un massimo di 60 secondi, il file viene infine eliminato. Tuttavia, trattenendo intenzionalmente l'ultimo pezzo di dati, un aggressore può mantenere il file temporaneo in uso, rendendolo sfruttabile.
Sebbene il contenuto del file caricato possa essere controllato, la sua localizzazione nel file system è difficile a causa del nome del file randomizzato. Il percorso di memorizzazione può essere configurato utilizzando client_body_temp_path, ma il nome del file viene generato casualmente in fase di esecuzione, rendendolo imprevedibile. Questa casualità ostacola in modo significativo l'accesso mirato anche attraverso la forza bruta. Per ovviare a questo problema, il team ha sfruttato i comportamenti intrinseci al sistema operativo Linux. Si consideri il seguente esempio:
This code opens a file and keeps it in an active state, closely mimicking the behavior of NGINX's client body buffer mechanism. Using /proc/{pid}/fd directory, attackers can find symbolic links created by the Linux kernel that map open file descriptors to their corresponding file paths. This route allows attackers to reduce the brute-force space to only two variables: the process ID (pid) and the file descriptor (fd).
Simulare lo sfruttamento
Sulla base dell'analisi precedente, un approccio pratico di sfruttamento per RCE all'interno del pod Ingress-NGINX è:
- Caricare una libreria condivisa dannosa utilizzando il meccanismo del body buffer del client di NGINX per memorizzarla temporaneamente sul file system.
- Utilizzare l'iniezione di template per avviare un tentativo di forza bruta che costringe NGINX a caricare la libreria condivisa precedentemente caricata tramite direttive vulnerabili.
Creazione del payload contenente la libreria condivisa
Per garantire l'esecuzione del codice al momento del caricamento, all'interno della libreria condivisa malevola viene definita una funzione entrypoint con estensione del costruttore. Questa funzione viene eseguita al momento del caricamento da NGINX ed è progettata per stabilire una connessione reverse shell a un host remoto.
Dopo la compilazione, la dimensione della libreria condivisa risultante superava comodamente gli 8KB, consentendo il buffering da parte di NGINX senza richiedere un padding aggiuntivo.
I nostri colleghi hanno quindi creato una richiesta con un valore di Content-Length gonfiato (ad esempio, 1MB) per introdurre un disallineamento delle dimensioni. Questo ha fatto sì che NGINX bufferizzasse l'intero corpo anziché elaborarlo immediatamente, assicurando che l'oggetto condiviso venisse scritto in una posizione prevedibile.
Attivazione della libreria condivisa tramite iniezione
Con la libreria condivisa al suo posto, abbiamo poi iniettato una direttiva dannosa nella configurazione di NGINX utilizzando il campo uid vulnerabile. Questa direttiva includeva ssl_engine che puntava al percorso del file bufferizzato:
Il successo dell'RCE richiede che la direttiva ssl_engine faccia riferimento al percorso corretto del file bufferizzato. Questo può essere ottenuto tramite uno script automatico di forza bruta che itera sistematicamente le possibili combinazioni di ID di processo e descrittori di file per identificare il collegamento simbolico valido che punta all'oggetto condiviso bufferizzato.
Di seguito è riportato un esempio del modello NGINX generato che ha innescato l'exploit.
Una volta sfruttato con successo, l'attaccante potrebbe ottenere l'accesso alla shell nel contesto del pod ingress-nginx, che per impostazione predefinita ha accesso a segreti sensibili del cluster Kubernetes.
Mitigazione e riparazione
Per mitigare efficacemente i rischi associati a CVE-2025-1974, le organizzazioni necessitano di una soluzione che fornisca visibilità e controllo sui propri componenti open-source.
OPSWAT SBOM, una tecnologia fondamentale della piattaforma MetaDefender®, risponde a questa esigenza fornendo un inventario di tutti i componenti software, le librerie, i container Docker e le dipendenze in uso. Ciò consente alle organizzazioni di tenere traccia, proteggere e aggiornare i propri componenti in modo proattivo.
Nell'esempio precedente, la tecnologia SBOM di MetaDefender Core™ ha analizzato il pacchetto nginx-ingress-controller che conteneva la vulnerabilità CVE-2025-1974. Il sistema ha automaticamente contrassegnato il problema come Critico e ha fornito indicazioni sulle versioni corrette disponibili, consentendo ai team di assegnare rapidamente le priorità e di applicare la patch alla vulnerabilità prima che possa essere sfruttata.
OPSWAT SBOM è disponibile in MetaDefender Core e MetaDefender Software Supply Chain™, consentendo ai team di sicurezza di identificare e agire più rapidamente sulle vulnerabilità. Con OPSWAT SBOM, i team di sicurezza possono:
- Individuazione rapida dei componenti vulnerabili - Identificazione immediata dei componenti open-source colpiti da attacchi di deserializzazione. In questo modo è possibile intervenire rapidamente per la patch o la sostituzione delle librerie vulnerabili.
- Garantire patch e aggiornamenti proattivi - Monitorare costantemente i componenti open-source tramite OPSWAT SBOM per essere sempre al passo con le vulnerabilità di deserializzazione. OPSWAT SBOM è in grado di rilevare componenti obsoleti o insicuri, consentendo aggiornamenti tempestivi e una minore esposizione agli attacchi.
- Mantenere la conformità e la reportistica - OPSWAT SBOM aiuta le organizzazioni a soddisfare i requisiti di conformità, in quanto i quadri normativi richiedono sempre più trasparenza nelle catene di fornitura del software.