
La RCE (Remote Code Execution, esecuzione remota di codice) è una vulnerabilità che consente a un attaccante di eseguire codice arbitrario all'interno di un sistema o di un'applicazione da remoto ed è classificata come CWE-94: Improper Control of Generation of Code ('Code Injection').
PyTorch Lightning è una libreria che aiuta a gestire facilmente l'addestramento di modelli di deep learning basati su PyTorch, mentre DeepDiff è una libreria che confronta due oggetti Python e ne analizza le differenze.
CVE-2024-5452 è una vulnerabilità che, nel processo di utilizzo di DeepDiff e Lightning nelle funzionalità dell'applicazione web relative ai pesi dei modelli AI di Lightning, provoca RCE durante la deserializzazione tramite la vulnerabile validazione delle intestazioni di DeepDiff e l'inquinamento delle proprietà delta.
Esamineremo il flusso di codice che porta alla vulnerabilità attraverso cui un attaccante può iniettare oggetti arbitrari o eseguire codice remoto (RCE) e cercheremo possibili contromisure.
In pytorch-lightning, è possibile attaccare l'endpoint /api/v1/delta tra gli endpoint DeepDiff del codice dell'applicazione web, inviando il delta con le sue proprietà inquinate.
Esaminando l'esempio, vogliamo capire come l'inquinamento delle proprietà dunder del delta possa causare la vulnerabilità di deserializzazione degli oggetti.
La prima richiesta è: esempio di attacco client, configurazione dell'inquinamento -> vulnerabile validazione dell'intestazione in /api/v1/delta di Deepdiff -> salvataggio dello stato tramite l'inquinamento.
[Figura 1] POC - Attacco all'endpoint
[Figura 2] POC - Iniezione della vulnerabilità / contenuto completo della configurazione dell'inquinamento
[Figura 3] lightning/api/core/api.py / parte della logica vulnerabile di controllo dell'intestazione
[Figura 4] lightning/api/core/app.py / sezione di configurazione
[Figura 5] lightning/api/core/app.py / sezione di salvataggio della configurazione
La prima richiesta aggira la validazione dell'header di /api/v1/delta nella [Figura 3] tramite la [Figura 1], inietta la configurazione di inquinamento manipolata (delta) della [Figura 2] e salva la configurazione tramite la [Figura 4] e la [Figura 5]. Ora vediamo come queste configurazioni delta manipulate agiscono nella seconda richiesta.
[Figura 6] lightning/api/core/app.py / flusso del percorso iniziale
La [Figura 6] mostra il flusso della seconda richiesta dopo la configurazione del delta: run_once() -> maybe_apply_change() -> _collect_deltas_from_ui_and_work_queues()
[Figura 7] Caso di inquinamento isinstance
Nella [Figura 7], il controllo isinstance in _collect_deltas_from_ui_and_work_queues() viene superato grazie alla configurazione inquinata (isinstance(delta, _DeltaRequest) == False), facendo sì che venga eseguito il ramo else.
[Figura 8] Caso di inquinamento isinstance 1
Nella [Figura 8], per facilitare la comprensione, vengono mostrati i risultati di isinstance(delta, _DeltaRequest), delta e _DeltaRequest (tipo). La prima richiesta è una richiesta di configurazione, quindi la modifica è in fase di impostazione ed è normale; nella richiesta successiva, invece, il tipo _DeltaRequest viene inquinato trasformandolo in str, aggirando così la condizione. Nel flusso, vediamo ora l'esempio del successivo obiettivo, _process_requests.
[Figura 9] Caso di inquinamento isinstance 2
Come si vede nella [Figura 9], con lo stesso meccanismo di cui sopra, _process_requests supera il controllo isinstance(request, _APIRequest) grazie alla configurazione inquinata proveniente dal codice POC del client a destra; questo inquinamento è un caso in cui la chiamata isinstance su un'istanza OrderSet restituisce true per str. Finora abbiamo visto due forme di inquinamento isinstance; nelle fasi successive la configurazione viene costruita in modo da poter richiamare le proprietà tramite l'inquinamento.
[Figura 10] Caso di inquinamento isinstance 3
Dopo aver completato l'aggiornamento della configurazione di isinstance e delle funzioni nella [Figura 10], si imposta la "fissazione dello stato interno" (lasciando vuoto _INTERNAL_STATE_VARS: ()) della [Figura 10], rendendo impossibile la verifica dello stato di configurazione interno nella [Figura 11].
[Figura 11_1] Funzione di controllo _INTERNAL_STATE_VARS in /lightning/app/core/flow.py
[Figura 11_2] __setattr__ in /lightning/app/core/flow.py
Con la [Figura 11] si conclude l'intero inquinamento e la configurazione.
[Figura 12] Comando RCE
[Figura 13] Risposta RCE
Infine, nella [Figura 13], viene chiamata exec con privilegi di root per eseguire il comando della [Figura 12] e concludere l'attacco.
Finora abbiamo esaminato il flusso con cui la vulnerabilità RCE (CVE-2024-5452) viene sfruttata tramite la deserializzazione degli oggetti nell'ambiente Lightning. Poiché si tratta di un attacco che porta alla compromissione del server stesso, le contromisure sono importanti. A tal fine, suggeriamo l'aggiornamento all'ultima versione.


Finora abbiamo analizzato la vulnerabilità che consente di provocare RCE inquinando le proprietà Dunder tramite Lightning e DeepDiff. Questa vulnerabilità si manifesta in primo luogo attraverso la vulnerabile validazione delle intestazioni e, successivamente, tramite la deserializzazione degli oggetti resa possibile dal vulnerabile controllo delle proprietà delta.
(poc) https://security.snyk.io/vuln/SNYK-PYTHON-PYTORCHLIGHTNING-7218866
(nist) https://nvd.nist.gov/vuln/detail/CVE-2024-5452