Los archivos de modelos y workflows suelen atravesar la empresa como si fueran datos: entran por un bucket, pasan por un registry y terminan en un job con acceso a GPU, datasets y credenciales de nube. La CVE-2025-33214 muestra el error de esa premisa. En NVIDIA NVTabular, cargar un workflow serializado podía ejecutar código en el mismo proceso que realizaba la carga.
La falla no requiere romper CUDA ni explotar la GPU. El límite falla antes, al deserializar un archivo workflow.pkl con cloudpickle. Si alguien puede reemplazar, publicar o indicar un artefacto que otra persona o automatización cargará, su contenido deja de ser mera configuración y obtiene la autoridad de la identidad que ejecuta el pipeline.

Qué confirma la CVE-2025-33214
NVIDIA asignó una puntuación CVSS 3.1 de 8,8, severidad alta, con vector AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H. En términos prácticos, la entrada puede llegar por la red, la explotación tiene baja complejidad y no requiere privilegios previos, pero depende de una acción de la víctima: el workflow malicioso debe cargarse. El impacto potencial incluye ejecución de código, indisponibilidad, exposición y alteración de datos.
- Producto afectado: NVIDIA NVTabular en Linux
- Componente vulnerable: carga de Workflow
- Versión probada por GitHub Security Lab: v23.08.00
- Alcance oficial: todas las versiones que no incluyen el commit 5dd11f4
- Causa clasificada: CWE-502, deserialización de datos no confiables
La línea donde los datos se convierten en ejecución
Workflow.load resuelve la ruta con fsspec y abre workflow.pkl. En la implementación analizada por GitHub Security Lab, el objeto se entregaba directamente a cloudpickle.load. Pickle no es un formato pasivo: durante la reconstrucción, puede importar clases y llamar funciones. Verificar la extensión o el nombre, o solo calcular un hash sin una referencia confiable, no cambia la naturaleza del archivo.
# Vulnerable implementation documented by GitHub Security Lab
workflow = cloudpickle.load(
fs.open(fs.sep.join([path, "workflow.pkl"]), "rb")
)
# Fix in commit 5dd11f4
from nvtabular.tools.serialization import load as load_safe
workflow = load_safe(
fs.open(fs.sep.join([path, "workflow.pkl"]), "rb")
)La corrección agrega un Unpickler propio y sobrescribe find_class. Cuando el pickle intenta reconstruir una clase o función fuera de la lista permitida, la carga termina con un error. Es una diferencia pequeña en el punto de llamada, pero cambia la pregunta de seguridad de '¿qué quiere importar este archivo?' a '¿esta importación fue autorizada explícitamente?'.

El vector real es la cadena de artefactos
CVSS registra interacción del usuario porque una persona o un job debe cargar el workflow. Eso no limita el escenario al phishing manual. En entornos de ML, la acción puede ser un notebook abierto por un investigador, una promoción automática entre buckets, un job de validación o un servicio que recibe la ruta del modelo. La prueba relevante es descubrir quién puede escribir o indicar el artefacto y qué identidad lo consume.
- Un registry o bucket permite publicar a identidades que no deberían influir en producción
- El pipeline promueve artefactos sin firma, procedencia o hash fijado
- Un notebook compartido carga workflows desde directorios modificables por otros usuarios
- Un job de inferencia o preparación de datos se ejecuta con credenciales más amplias de lo necesario
- Los logs registran la ejecución del job, pero no el digest y el origen exacto del artefacto cargado
Cómo probar sin ejecutar un pickle malicioso
En un pentest, no necesitamos colocar un payload de ejecución en un entorno compartido para demostrar el riesgo. Primero confirmamos la revisión del código y el punto de carga mediante inspección estática. Después mapeamos permisos de escritura y lectura del registry, identidad del consumidor y promoción entre entornos. Una prueba segura puede usar un artefacto inválido e inerte para demostrar que un origen no confiable alcanza el loader, sin incluir un opcode capaz de llamar al sistema operativo.
# Run in the NVTabular checkout used to build the image.
# These commands only read code and Git history.
rg -n 'cloudpickle\.load|load_safe' nvtabular/workflow/workflow.py
if git merge-base --is-ancestor 5dd11f4 HEAD; then
echo 'fix 5dd11f4 is present in history'
else
echo 'fix 5dd11f4 was not confirmed in this checkout'
fi
git rev-parse HEADEncontrar load_safe es evidencia útil, pero no completa el análisis. El binario o contenedor en producción debe corresponder al código revisado, y la lista de clases permitidas también merece inspección. En deserialización, cada nueva clase autorizada vuelve a ampliar la superficie disponible durante la reconstrucción.
Corrección y contención
La orientación oficial es actualizar a una branch que incluya el commit 5dd11f4 o posterior. NVIDIA no identifica una versión numerada corregida en el boletín, por lo que revisar solo la versión mostrada por el paquete puede ser insuficiente. Se debe validar el commit incorporado, la imagen de contenedor aprobada o una confirmación equivalente del mantenedor de la distribución utilizada.
- Actualiza NVTabular a código que contenga 5dd11f4 o posterior y reconstruye las imágenes derivadas
- Invalida workflows de origen desconocido y vuelve a publicar artefactos desde un pipeline confiable
- Exige digest inmutable, firma y procedencia antes de promover a producción
- Separa a quienes publican artefactos de las identidades que autorizan su ejecución en entornos privilegiados
- Reduce credenciales, mounts y acceso de red del proceso que carga workflows
- Registra digest, origen, identidad publicadora e identidad consumidora en cada carga
La lección para cualquier pipeline de ML
La CVE-2025-33214 es específica de NVTabular, pero el patrón es más amplio: modelos, checkpoints y workflows serializados pueden contener comportamiento, no solo pesos o configuración. Si el proceso de ingestión confía en la extensión del archivo y el proceso de ejecución confía en el loader, toda la cadena delega ejecución a quien controla el artefacto.
Un pentest de este entorno debe seguir la ruta completa del objeto, desde la publicación hasta el runtime. La pregunta decisiva no es solo '¿el paquete está corregido?', sino '¿quién puede hacer que este proceso privilegiado cargue qué?'. En esa conexión entre storage, identidad, automatización y runtime, una falla de deserialización se convierte en compromiso de infraestructura.