Cyberattacchi alimentati dall'intelligenza artificiale: Come individuare, prevenire e difendersi dalle minacce intelligenti

Leggi ora
Per le traduzioni dei siti utilizziamo l'intelligenza artificiale e, sebbene ci sforziamo di essere accurati, non sempre le traduzioni sono precise al 100%. La vostra comprensione è apprezzata.

Salvaguardia dagli attacchi di deserializzazione in Spring per Apache Kafka

da OPSWAT
Condividi questo post
Un'immagine professionale di Nguyen Phu Hung e Tran Anh Huy, che rappresenta le loro affiliazioni con HUTECH e HCMC University of Science.

Gli attacchi di deserializzazione, ovvero la manipolazione non autorizzata di dati serializzati per eseguire codice dannoso, possono causare gravi danni alle organizzazioni. Gli sviluppatori e i team di sicurezza devono identificare in modo proattivo le vulnerabilità chiave che rivelano come, quando e dove si è verificato l'attacco.  

Un esempio recente di questa minaccia è CVE-2023-34040, un vettore di attacco di deserializzazione in Spring per Apache Kafka che può portare a RCE (esecuzione di codice remoto). In effetti, la prevalenza delle vulnerabilità di deserializzazione è evidenziata dall'elenco OWASP Top 10, che include la "Deserializzazione di dati non attendibili" tra i 10 rischi più critici per la sicurezza delle applicazioni web.  

Strumenti come OPSWAT MetaDefender Core™ con il suo motore SBOMSoftware Bill of Materials) sono essenziali per rilevare e prevenire gli attacchi di deserializzazione. Questi strumenti consentono agli sviluppatori di scansionare e analizzare in modo efficiente il loro codice, assicurando che nessuna vulnerabilità venga trascurata.  

In questo blog, i nostri borsisti discutono i dettagli di CVE-2023-34040 e del suo sfruttamento, e di come proteggere i componenti open-source da minacce simili. 

Informazioni su CVE-2023-34040

La CVE-2023-34040 rivela un vettore di attacco di deserializzazione in Spring per Apache Kafka, che può essere sfruttato quando viene applicata una configurazione insolita. Questa vulnerabilità consente a un utente malintenzionato di costruire un oggetto serializzato dannoso in una delle intestazioni dei record di eccezione di deserializzazione, che può provocare un RCE. La vulnerabilità riguarda le versioni di Spring for Apache Kafka da 2.8.1 a 2.9.10 e da 3.0.0 a 3.0.9.

Una schermata che mostra i punteggi di gravità CVSS 3.x per le vulnerabilità, con i livelli di rischio alto e medio di NIST e VMware.

In particolare, un'applicazione/consumatore è vulnerabile nelle seguenti configurazioni e condizioni specifiche: 

  • La classe ErrorHandlingDeserializer non è configurata per la chiave e/o il valore del record. 
  • Le proprietà checkDeserExWhenKeyNull e/o checkDeserExWhenValueNull del consumatore sono impostate su true. 
  • Le fonti non attendibili possono pubblicare su un argomento Kafka.

Apache Kafka

Apache Kafka, sviluppato dalla Apache Software Foundation, è una piattaforma distribuita di streaming di eventi progettata per catturare, elaborare, rispondere e instradare flussi di dati in tempo reale provenienti da varie fonti, tra cui database, sensori e dispositivi mobile . 

Ad esempio, può inviare notifiche in streaming ai servizi che reagiscono alle attività dei clienti, come il completamento del checkout di un prodotto o l'esecuzione di un pagamento.

Un diagramma che illustra una pipeline di elaborazione dati basata su Apache Kafka, che collega i microservizi di prodotto ai microservizi di SMS, notifiche push ed e-mail.

In Apache Kafka, un evento - chiamato anche record o messaggio - serve come unità di dati che rappresenta un evento nell'applicazione ogni volta che i dati vengono letti o scritti. Ogni evento include una chiave, un valore, un timestamp e intestazioni di metadati opzionali.

Chiave binaria
(può essere nullo)
Valore-binario
(può essere nullo)
Tipo di compressione
[nessuno, gzip, snappy, lz4, zstd].
Intestazioni (facoltative)
ChiaveValore
ChiaveValore
Partizione + offset
Timestamp (impostato dal sistema o dall'utente)

Gli eventi sono memorizzati in modo duraturo e organizzati in argomenti. Le applicazioni client che inviano (scrivono) eventi agli argomenti di Kafka sono chiamate produttori, mentre quelle che sottoscrivono (leggono ed elaborano) gli eventi sono note come consumatori. 

Un diagramma di flusso che mostra come l'input dell'utente viene elaborato attraverso un produttore, registrato, consumato e consegnato a un endpoint.

Spring per Apache Kafka

Per collegare Apache Kafka all'ecosistema Spring, gli sviluppatori possono utilizzare Spring for Apache Kafka, che semplifica l'integrazione nelle applicazioni Java.  

Spring per Apache Kafka offre strumenti e API robusti che semplificano il processo di invio e ricezione di eventi con Kafka, consentendo agli sviluppatori di svolgere queste attività senza una codifica estesa e complessa.

Un grafico che mostra i loghi di Spring Boot e Apache Kafka, a indicare l'integrazione per le applicazioni event-driven.

Serializzazione e deserializzazione

La serializzazione è un meccanismo di conversione dello stato di un oggetto in una stringa o in un flusso di byte. La deserializzazione, invece, è il processo inverso, in cui i dati serializzati vengono riconvertiti nell'oggetto o nella struttura di dati originale. La serializzazione consente di trasformare dati complessi in modo che possano essere salvati in un file, inviati in rete o archiviati in un database. La serializzazione e la deserializzazione sono essenziali per lo scambio di dati nei sistemi distribuiti e favoriscono la comunicazione tra i vari componenti di un'applicazione software. In Java, writeObject() viene utilizzato per la serializzazione e readObject() per la deserializzazione.

Un diagramma tecnico che mostra come gli oggetti di dati vengono serializzati in memoria, file o database e successivamente deserializzati in oggetti.

Poiché la deserializzazione consente di convertire un flusso di byte o una stringa in un oggetto, una gestione impropria o la mancanza di un'adeguata convalida dei dati in ingresso può causare una significativa vulnerabilità della sicurezza, che può portare a un attacco RCE.

Analisi delle vulnerabilità

Secondo la descrizione del CVE, i borsisti OPSWAT hanno configurato checkDeserExWhenKeyNull e checkDeserExWhenValueNull su true per attivare la vulnerabilità di sicurezza. Inviando un record con una chiave/valore vuota e conducendo un'analisi dettagliata con il debug del consumatore mentre riceveva un record Kafka dal produttore, i nostri borsisti hanno scoperto il seguente flusso di lavoro durante l'elaborazione del record: 

Fase 1: Ricezione dei record (messaggi)

Quando riceve i record, il consumatore invoca il metodo invokeIfHaveRecords(), che poi chiama il metodo invokeListener() per attivare un ascoltatore di record registrato (una classe annotata con l'annotazione @KafkaListener ) per l'elaborazione effettiva dei record. 

Una schermata del codice Java che definisce un contenitore di ascoltatori di messaggi Kafka per gestire i record in un sistema guidato dagli eventi.

Il metodo invokeListener() richiama quindi il metodo invokeOnMessage().

Fase 2: Controllo dei registri

Nel metodo invokeOnMessage(), vengono valutate diverse condizioni rispetto al valore del record e alle proprietà di configurazione, che determinano il passo successivo da eseguire.

Un frammento di codice Java evidenziato che dimostra la gestione delle eccezioni per la deserializzazione dei messaggi in un consumatore Kafka.

Se un record ha una chiave o un valore nullo e le proprietà checkDeserExWhenKeyNull e/o checkDeserExWhenValueNull sono esplicitamente impostate su true, verrà richiamato il metodo checkDeser() per esaminare il record.

Passo 3: Controllo dell'eccezione dalle intestazioni

In checkDesr(), il consumatore invoca continuamente getExceptionFromHeader() per recuperare eventuali eccezioni dai metadati del record, se presenti, e memorizza il risultato in una variabile chiamata exception.

Il metodo Java checkDeser chiama getExceptionFromHeader per gestire le eccezioni di deserializzazione nell'elaborazione dei messaggi Kafka.

Passo 4: Estrarre l'eccezione dalle intestazioni

Il metodo getExceptionFromHeader() è progettato per estrarre e restituire un'eccezione dall'intestazione di un record Kafka. Per prima cosa recupera l'intestazione del record e poi ottiene il valore dell'intestazione, memorizzato in un array di byte.

Il metodo Java getExceptionFromHeader estrae le eccezioni di deserializzazione dalle intestazioni dei messaggi Kafka.

Successivamente, inoltra l'array di byte del valore dell'intestazione al metodo byteArrayToDeserializationException() per un'ulteriore gestione. 

Passo 5: Deserializzazione dei dati

Nella byteArrayToDeserializationException(), la funzione resolveClass() viene sovrascritta per limitare la deserializzazione alle sole classi consentite. Questo approccio impedisce la deserializzazione di qualsiasi classe non esplicitamente consentita. Il valore dell'array di byte dell'intestazione può essere deserializzato all'interno di byteArrayToDeserializationException() solo se soddisfa la condizione impostata in resolveClass(), che consente la deserializzazione esclusivamente per la classe DeserializationException.

Il metodo Java byteArrayToDeserializationException converte un array di byte in un'eccezione di deserializzazione.

Tuttavia, la classe DeserializationException estende la classe Exception standard e include un costruttore con quattro parametri. L'ultimo parametro, causa, rappresenta l'eccezione originale che ha scatenato la DeserializationException, come una IOException o una ClassNotFoundException.

Costruttore Java per DeserializationException con parametri per messaggio, dati, flag di chiave e causa lanciabile

La classe Throwable funge da superclasse per tutti gli oggetti che possono essere lanciati come eccezioni o errori in Java. Nel linguaggio di programmazione Java, le classi di gestione delle eccezioni come Throwable, Exception ed Error possono essere deserializzate in modo sicuro. Quando un'eccezione viene deserializzata, Java consente di caricare e istanziare il genitore delle classi Throwable con controlli meno impegnativi di quelli applicati alle classi normali. 

In base al flusso di lavoro e all'analisi completa, se i dati serializzati corrispondono a una classe dannosa che eredita dalla classe genitore Throwable, possono aggirare i controlli di condizione. Ciò consente la deserializzazione di un oggetto dannoso, che può eseguire codice dannoso e potenzialmente portare a un attacco RCE.

Sfruttamento

Diagramma che mostra il processo di sfruttamento di un cyberattacco, dall'iniezione di dati dannosi da parte di un attaccante all'esecuzione di codice remoto.

Come indicato nell'analisi, lo sfruttamento di questa vulnerabilità richiede la generazione di dati dannosi inviati al consumatore tramite il record di intestazione di Kafka. Inizialmente, l'aggressore deve creare una classe dannosa che estende la classe Throwable e poi utilizzare una catena di gadget per ottenere l'esecuzione di codice remoto. I gadget sono frammenti di codice sfruttabili all'interno dell'applicazione e, concatenandoli, l'aggressore può raggiungere un "gadget di scarico" che innesca azioni dannose. 

Di seguito è riportata una classe dannosa che può essere utilizzata per sfruttare questa vulnerabilità in Spring per Apache Kafka: 

La classe Java CustomExceptionClass dimostra una vulnerabilità nell'esecuzione del payload utilizzando i comandi PowerShell.

Successivamente, viene creata un'istanza della classe maligna e passata come argomento al parametro cause nel costruttore della classe DeserializationException. L'istanza di DeserializationException viene quindi serializzata in un flusso di byte, che viene successivamente utilizzato come valore nell'intestazione del record Kafka dannoso.

Classe Java per la costruzione di un oggetto serializzato dannoso per gli exploit di deserializzazione
Il metodo Java sendMessage costruisce e invia un messaggio Kafka, iniettando un payload serializzato dannoso se sia la chiave che i dati sono nulli.

Se l'aggressore riesce a ingannare la vittima e a farle usare il suo produttore maligno, può controllare i record Kafka inviati al consumatore, creando l'opportunità di compromettere il sistema. 

Interfaccia utente per l'invio di messaggi Kafka, che mostra un modulo di messaggio e i messaggi ricevuti da due produttori.

Quando il consumatore vulnerabile riceve un record Kafka dal produttore dannoso contenente chiavi e valori nulli, insieme a un'istanza serializzata dannosa nell'intestazione del record, il consumatore elabora il record, compreso il processo di deserializzazione. Questo porta all'esecuzione di codice remoto, consentendo all'aggressore di compromettere il sistema.

Terminale e interfaccia del mittente di messaggi Kafka, dimostrazione dell'iniezione di messaggi con l'esecuzione di un comando in ambiente Windows.

Mitigazione di CVE-2023-34040 con SBOM in MetaDefender Core

Per mitigare efficacemente i rischi associati a CVE-2023-34040, le organizzazioni necessitano di una soluzione completa che fornisca visibilità e controllo sui propri componenti open-source.  

SBOM, una tecnologia fondamentale di MetaDefender Core, fornisce una risposta efficace. Agendo come un inventario completo di tutti i componenti software, le librerie e le dipendenze in uso, SBOM consente alle organizzazioni di monitorare, proteggere e aggiornare i propri componenti open-source in modo proattivo ed efficiente.

Schermata di un dashboard che mostra un file XML bloccato con vulnerabilità critiche ed elevate

Con SBOM, i team di sicurezza possono:

  • Individuare rapidamente i componenti vulnerabili: Identificare immediatamente i componenti open-source colpiti da attacchi di deserializzazione. Questo garantisce un'azione rapida per la patch o la sostituzione delle librerie vulnerabili. 
  • Garantire patch e aggiornamenti proattivi: Monitorare continuamente i componenti open-source tramite SBOM per essere sempre all'avanguardia rispetto alle vulnerabilità di deserializzazione. SBOM è in grado di rilevare componenti obsoleti o insicuri, consentendo aggiornamenti tempestivi e riducendo l'esposizione agli attacchi. 
  • Mantenere la conformità e la reportistica: SBOM aiuta le organizzazioni a soddisfare i requisiti di conformità, in quanto i quadri normativi impongono sempre più spesso la trasparenza nelle catene di fornitura del software.

Pensieri conclusivi

Le vulnerabilità di deserializzazione sono una minaccia significativa per la sicurezza che può essere utilizzata per sfruttare un'ampia gamma di applicazioni. Queste vulnerabilità si verificano quando un'applicazione deserializza dati dannosi, consentendo agli aggressori di eseguire codice arbitrario o accedere a informazioni sensibili. La vulnerabilità CVE-2023-34040 di Spring per Apache Kafka ci ricorda i pericoli degli attacchi di deserializzazione. 

Per prevenire gli attacchi di deserializzazione, è essenziale implementare strumenti avanzati come OPSWAT MetaDefender Core e la sua tecnologia SBOM. Le organizzazioni possono ottenere una visibilità approfondita della loro catena di fornitura del software, garantire la tempestiva correzione delle vulnerabilità e proteggersi da un panorama di minacce in continua evoluzione. Proteggere in modo proattivo i componenti open-source non è solo una best practice, ma una necessità per proteggere i sistemi moderni da potenziali sfruttamenti.


Rimanete aggiornati con OPSWAT!

Iscriviti oggi stesso per ricevere gli ultimi aggiornamenti sull'azienda, storie, informazioni sugli eventi e altro ancora.