
RCE (Remote Code Execution, ejecución remota de código) es una vulnerabilidad que permite a un atacante ejecutar código arbitrario de forma remota dentro de un sistema o aplicación, y corresponde a CWE-94: Improper Control of Generation of Code ('Code Injection').
PyTorch Lightning es una librería que facilita la gestión del entrenamiento de modelos de deep learning basados en PyTorch, y DeepDiff es una librería que compara dos objetos de Python para analizar sus diferencias.
CVE-2024-5452 es una vulnerabilidad que, en las funciones de la aplicación web relacionadas con los pesos de los modelos de IA de Lightning, al utilizar DeepDiff y Lightning, provoca una RCE durante la deserialización mediante la validación deficiente de cabeceras de DeepDiff y la contaminación de atributos delta.
Por ello, examinaremos el flujo de código que genera la vulnerabilidad que permite a un atacante inyectar objetos arbitrarios o ejecutar código remoto (RCE), y buscaremos medidas de mitigación.
En pytorch-lightning, se puede atacar contaminando las propiedades de delta enviadas al endpoint /api/v1/delta de DeepDiff en el código fuente de la aplicación web.
A través de un ejemplo, veremos cómo la contaminación de las propiedades dunder de delta provoca una vulnerabilidad de deserialización de objetos.
La primera solicitud consiste en: ejemplo de ataque del cliente, configuración de la contaminación -> validación deficiente de cabeceras en /api/v1/delta de DeepDiff -> guardado de estado mediante la configuración de la contaminación.
[Figura 1] POC - Ataque al endpoint
[Figura 2] POC - Inyección de vulnerabilidad / Contenido completo de la configuración de contaminación
[Figura 3] lightning/api/core/api.py / Parte de la lógica de validación deficiente de cabeceras
[Figura 4] lightning/api/core/app.py / Sección de configuración
[Figura 5] lightning/api/core/app.py / Sección de guardado de configuración
La primera solicitud elude la validación de cabeceras de /api/v1/delta de la [Figura 3] mediante la [Figura 1], inyecta la configuración de contaminación manipulada (delta) de la [Figura 2] y guarda la configuración a través de las [Figura 4] y [Figura 5]. Ahora veamos cómo actúan estas configuraciones delta manipuladas en la segunda solicitud.
[Figura 6] lightning/api/core/app.py / Flujo de rutas inicial
La [Figura 6] muestra el flujo de la segunda solicitud después de configurar el delta: run_once() -> maybe_apply_change() -> _collect_deltas_from_ui_and_work_queues()
[Figura 7] Caso de contaminación de isinstance
En la [Figura 7], la comprobación isinstance en _collect_deltas_from_ui_and_work_queues() se hace pasar mediante la configuración contaminada (isinstance(delta, _DeltaRequest) == False), por lo que se ejecuta la rama else.
[Figura 8] Caso de contaminación de isinstance 1
En la [Figura 8], para facilitar la comprensión, se imprimen los resultados de isinstance(delta, _DeltaRequest), delta y _DeltaRequest (tipo). La primera solicitud es una solicitud de configuración que está estableciendo la modificación, por lo que es normal; en la siguiente solicitud, el tipo _DeltaRequest está contaminado como str, eludiendo la condición. Siguiendo con el flujo, veamos el ejemplo de _process_requests, el siguiente objetivo.
[Figura 9] Caso de contaminación de isinstance 2
Como se puede ver en la [Figura 9], con el mismo contenido que el caso anterior, _process_requests hace pasar la comprobación isinstance(request, _APIRequest) mediante la configuración contaminada del código POC del cliente de la derecha. Esta contaminación es un caso en el que la llamada isinstance con str sobre una instancia de OrderSet devuelve True. Hasta ahora hemos visto dos formas de contaminación de isinstance; lo que sigue es construir la configuración para poder invocar propiedades mediante la contaminación.
[Figura 10] Caso de contaminación de isinstance 3
Tras completar la elusión de isinstance y la configuración de funciones de la [Figura 10], se configura el «fijado de estado interno» de la [Figura 10] (dejando vacío _INTERNAL_STATE_VARS: ()) para que no se pueda verificar el estado de configuración interna en la [Figura 11].
[Figura 11_1] Función de comprobación de _INTERNAL_STATE_VARS en /lightning/app/core/flow.py
[Figura 11_2] __setattr__ en /lightning/app/core/flow.py
Con la [Figura 11] como último paso, la contaminación y la configuración generales han terminado.
[Figura 12] Comando RCE
[Figura 13] Respuesta RCE
Finalmente, en la [Figura 13] se llama a exec con privilegios de root para ejecutar el comando de la [Figura 12], concluyendo así el ataque.
Hasta ahora hemos revisado el flujo en el que se ejecuta la vulnerabilidad RCE mediante la deserialización de objetos (CVE-2024-5452) en el entorno de Lightning. Dado que se trata de un ataque en el que el propio servidor se ve comprometido, las medidas de mitigación son importantes. Para ello, recomendamos actualizar a la última versión.


Hasta ahora hemos analizado la vulnerabilidad que, mediante la contaminación de propiedades dunder a través de Lightning y DeepDiff, provoca RCE. Esta vulnerabilidad se produce, en primer lugar, por la validación deficiente de cabeceras y, en segundo lugar, por la deserialización de objetos a través de la inspección deficiente de las propiedades de delta.
(poc) https://security.snyk.io/vuln/SNYK-PYTHON-PYTORCHLIGHTNING-7218866
(nist) https://nvd.nist.gov/vuln/detail/CVE-2024-5452