Bucket squatting dans Vertex AI : la RCE cross-tenant « Pickle in the Middle »
Unit 42 a divulgué (16 juin 2026) une faille du SDK Python Vertex AI : un nom de bucket de staging prévisible et l'absence de vérification de propriété permettaient de détourner l'upload d'un modèle et d'obtenir une exécution de code cross-tenant. Corrigé en v1.148.0.
De quoi s’agit-il ?
Le 16 juin 2026, Unit 42 de Palo Alto Networks (chercheur Ori Hadad) a publié Pickle in the Middle, la divulgation d’une vulnérabilité dans le SDK Python de Vertex AI (google-cloud-aiplatform) de Google Cloud. Avant le correctif de Google, la faille permettait à un attaquant opérant entièrement depuis son propre projet Google Cloud — avec zéro accès au projet de la victime — de détourner l’upload d’un modèle de machine learning, de l’empoisonner et d’obtenir une exécution de code à distance dans l’infrastructure de service de Vertex AI.
Le bug a été signalé via le Vulnerability Reward Program de Google le 5 mars 2026 et entièrement corrigé avant la divulgation publique : un premier correctif en v1.144.0 (31 mars 2026), un second en v1.148.0 (15 avril 2026). Les versions affectées étaient 1.139.0 et 1.140.0. Il s’agit d’un problème entièrement divulgué et corrigé — nous couvrons le mécanisme et la leçon, pas un exploit actionnable.
Comment ça marche
L’attaque enchaîne deux faiblesses d’apparence anodine.
D’abord, un nom de bucket par défaut prévisible. Quand un développeur uploade un modèle sans spécifier de staging_bucket, le SDK construit un nom de bucket Cloud Storage selon un schéma déterministe fondé sur l’ID de projet et la région. Comme les noms de buckets GCS sont uniques au niveau mondial sur tout Google Cloud, quiconque peut deviner ce nom peut le pré-créer dans son propre projet — une classe d’attaque appelée bucket squatting (la même logique de collision de noms que le slopsquatting et la confusion de dépendances).
Ensuite, une vérification de propriété manquante. Le SDK appelait seulement bucket.exists() et, trouvant le bucket (appartenant à l’attaquant) déjà présent, y déposait silencieusement les artefacts de la victime. L’attaquant accorde les rôles IAM adéquats pour que l’upload de la victime et l’agent de service Vertex AI réussissent tous deux — rien ne semble cassé.
Vient ensuite une course contre la montre. L’attaquant arme une Cloud Function sur l’événement google.storage.object.finalize : dès que le model.joblib de la victime arrive, il est remplacé par un artefact malveillant. D’après le proof of concept d’Unit 42, la fenêtre entre l’upload de la victime et la lecture du fichier par le Per-Product, Per-Project Service Account (P4SA) est d’environ 2,5 secondes, tandis que la fonction déclenchée par l’événement réagit en environ 800 ms — assez pour gagner la course à chaque fois.
Le payload lui-même exploite une propriété bien connue de la sérialisation Python : les modèles ML sont couramment stockés avec pickle (ou son wrapper Joblib), et pickle.load() / joblib.load() exécutent une méthode __reduce__ avant toute validation. Quand l’agent de service de Vertex AI désérialisait le modèle remplacé, le code de l’attaquant s’exécutait dans le projet tenant géré par Google. Dans le test d’Unit 42, ce code a récupéré le jeton OAuth du conteneur de service depuis le serveur de métadonnées GCE — un jeton qui atteignait d’autres artefacts, des métadonnées BigQuery, des journaux du tenant et des noms de clusters GKE dans le même tenant géré. C’est le même sink de désérialisation non sûre qu’on retrouve dans toute la pile ML, de l’injection de config Transformers à la RCE du parser GGUF et au contournement trust-remote-code de vLLM.
Pourquoi c’est important
La victime ne fait rien de mal. La reproduction utilise des appels SDK standard sans configuration inhabituelle — Model.upload() suivi de deploy(). La vulnérabilité résidait entièrement dans l’outillage de la plateforme, et pourtant le rayon d’impact était une exécution de code cross-tenant et un vol d’identifiants sans aucun pied dans la place, avec seulement la connaissance d’un ID de projet et d’une région (des valeurs loin d’être secrètes).
Elle déplace aussi la menace. L’essentiel de l’attention en sécurité LLM se concentre sur les prompts et les agents ; ce cas rappelle que la chaîne d’approvisionnement des modèles et l’outillage cloud du développeur sont des surfaces d’attaque de premier plan. Une convention de nommage plus une vérification de propriété omise suffisent à transformer un simple model.upload() en RCE dans l’infrastructure même du fournisseur — un cousin des problèmes de privilège d’agent de service Vertex AI documentés précédemment.
Défenses
- Mettez à jour le SDK. Passez
google-cloud-aiplatformen v1.148.0 ou ultérieure. Les correctifs ajoutent unuuid4aléatoire au nom du bucket de staging (fin de la prévisibilité) et une vérification de propriété du bucket avant usage. - Fixez un bucket de staging explicite et possédé. Définissez toujours
staging_bucketsur un emplacement Cloud Storage de votre projet plutôt que de vous fier au défaut déterministe. Unit 42 recommande cette pratique même sur SDK corrigé. - Traitez les artefacts de modèle comme du code non fiable. Tout ce qui est désérialisé via pickle/Joblib peut s’exécuter au chargement. Préférez des formats sûrs (par ex.
safetensors) quand c’est possible, vérifiez l’intégrité des artefacts (hachages/signatures) avant déploiement, et ne chargez de modèles non fiables que dans des contextes sandboxés à moindre privilège. - Limitez la portée des agents de service et des métadonnées. Restreignez ce que les identités des conteneurs de service peuvent atteindre ; limitez l’exposition du serveur de métadonnées et l’egress pour qu’un pied de désérialisation ne puisse pas pivoter vers des jetons et des ressources cross-tenant.
- Surveillez la classe des collisions de noms en général. Le squatting de buckets/paquets/namespaces exploite toujours des hypothèses d’unicité globale. Auditez tout outillage qui dérive un nom de ressource globalement unique à partir d’entrées devinables sans vérifier la propriété.
Statut
| Élément | Détail |
|---|---|
| Divulgation | 5 mars 2026 (VRP Google) ; public le 16 juin 2026 |
| Affecté | google-cloud-aiplatform 1.139.0, 1.140.0 |
| Premier correctif | v1.144.0 (uuid4 aléatoire dans le nom de bucket) — 31 mars 2026 |
| Correctif complet | v1.148.0 (vérification de propriété du bucket) — 15 avril 2026 |
| Impact | RCE cross-tenant, empoisonnement de modèle, vol d’identifiants — zéro accès au projet victime |
| Exploitation observée | Non rapportée |
La leçon n’est pas « corrigez Vertex AI et passez à autre chose ». C’est que les plateformes d’IA héritent de toutes les faiblesses classiques du cloud — noms de ressources prévisibles, vérifications de propriété manquantes, désérialisation non sûre — et que le chemin d’upload des modèles fait désormais partie de votre surface d’attaque. Les défenseurs qui fixaient déjà des buckets possédés, vérifiaient les artefacts et traitaient les modèles sérialisés comme du code exécutable n’étaient jamais exposés.
Sources
- → https://unit42.paloaltonetworks.com/hijacking-vertex-ai-model/
- → https://www.csoonline.com/article/4186193/googles-vertex-ai-sdk-could-allow-rce-through-bucket-squatting.html
- → https://thehackernews.com/2026/06/google-vertex-ai-sdk-flaw-let-attackers.html
- → https://github.com/googleapis/python-aiplatform/releases/tag/v1.148.0