flawopen.com/Teardowns/cve-2026-105752-vllm-harmony-cache-salt-omission
CVE-2026-105752 : omission de cache_salt dans vLLM
Une continuation d’outil GPT-OSS dans Responses de vLLM pouvait omettre cache_salt et utiliser un espace de cache de préfixes sans salt. Passez à vLLM 0.30.0 ou ultérieur et conservez les salts par locataire dans chaque continuation.[1], [5]
Imaginez les documents de chaque client rangés dans des casiers portant une étiquette de couleur privée. La première requête a la bonne étiquette, mais un outil la perd en rendant les documents pour l’étape suivante. Une étagère partagée peut indiquer par un succès de cache si des pages supposées s’y trouvent déjà ; elle ne remet pas les pages.[1]
Concepts Clés et Termes
Prefix caching- Réutilise les blocs clé/valeur du modèle pour des préfixes identiques.[6]
cache_salt- Valeur intégrée au hachage du premier bloc pour séparer les caches aux salts différents.[6]
Harmony continuation- Entrée suivante du modèle après une réponse Harmony, y compris après un outil.[1]
Membership oracle- Signal de présence d’un préfixe supposé sans révéler son contenu.[1]
CWE-524- Utilisation d’un cache contenant des informations sensibles ; classification principale de l’avis.[1]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-524
Analyse de Cause Racine
L’avis vLLM décrit GPT-OSS Harmony sur POST /v1/responses. L’entrée initiale transmettait request.cache_salt à tokens_input ; après un outil intégré ou MCP, la continuation utilisait tokens_input(token_ids) sans salt. Le helper ne stocke le salt que s’il est fourni. Avec le cache automatique des préfixes dans un déploiement partagé, la continuation utilisait donc l’espace sans salt. L’avis cite les versions antérieures à 0.30.0, confirme le comportement en 0.25.1 et précise que la borne basse n’est pas établie. Le résultat est un signal d’appartenance au cache via cached_tokens_per_turn, pas la divulgation directe d’un texte de prompt arbitraire. CWE-200 est aussi cité comme impact ; CWE-524 est l’unique faiblesse principale ici.[1], [3], [4]
Déroulement de l'Attaque Étape par Étape
Un service partagé réunit les prérequis
Il faut une version concernée, GPT-OSS Harmony Responses, le cache de préfixes et un outil ; la victime définit cache_salt et atteint la continuation.[1]
La première entrée du moteur reçoit le salt
L’appel initial de tokens_input inclut request.cache_salt et place le premier préfixe dans l’espace salé de l’appelant.[1]
La continuation de l’outil perd le salt
Après le résultat de l’outil, la continuation appelle tokens_input(token_ids) sans le salt initial ; le préfixe peut rejoindre l’espace partagé non salé.[1]
Un compteur de cache peut révéler une appartenance
Un locataire qui reconstitue le préfixe après l’outil peut consulter cached_tokens_per_turn pour déduire son appartenance, pas récupérer un prompt arbitraire.[1]
Code Source : Vulnérable vs Sécurisé
L’avis vLLM décrit GPT-OSS Harmony sur POST /v1/responses. L’entrée initiale transmettait request.cache_salt à tokens_input ; après un outil intégré ou MCP, la continuation utilisait tokens_input(token_ids) sans salt. Le helper ne stocke le salt que s’il est fourni. Avec le cache automatique des préfixes dans un déploiement partagé, la continuation utilisait donc l’espace sans salt. L’avis cite les versions antérieures à 0.30.0, confirme le comportement en 0.25.1 et précise que la borne basse n’est pas établie. Le résultat est un signal d’appartenance au cache via cached_tokens_per_turn, pas la divulgation directe d’un texte de prompt arbitraire. CWE-200 est aussi cité comme impact ; CWE-524 est l’unique faiblesse principale ici. Upstream excerpts, not a literal line-by-line diff.[1], [3], [4], [7]
Extrait source: renderer Harmony Responses de vLLM (extraits de versions distinctes)
# Le chemin de la requête initiale utilisait le salt, contrairement à la continuation après l’outil.
engine_input = tokens_input(token_ids, cache_salt=request.cache_salt)
# Après le résultat d’un outil, la continuation reconstruisait l’entrée ainsi :
engine_input = tokens_input(token_ids)
# Conserver le salt de la requête dans le contexte de rendu Harmony.
cache_salt = cast(str | None, engine_input.get("cache_salt"))
engine_input = self.online_renderer.render_responses_harmony_messages(
context.messages,
cache_salt=cache_salt,
tok_params=tok_params,
)
# Le moteur de rendu fournit le salt chaque fois qu’il crée l’entrée du moteur.
engine_input = tokens_input(prompt["prompt_token_ids"], cache_salt=cache_salt)
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Passez à vLLM 0.30.0 ou ultérieur et vérifiez les versions en cours.[1], [5]
- ✓Générez un cache_salt imprévisible dans la passerelle de confiance et transmettez-le à chaque requête et continuation.[6], [1]
- ✓Testez que chaque continuation Harmony après outil conserve le salt et sépare les locataires.[7]
- ✓Pour une isolation stricte, séparez les locataires par processus ou déploiement.[6]
- ✓Vérifiez les fonctions partagées et limitez l’accès aux métriques cached_tokens_per_turn.[1]
- ✓Décrivez le risque comme une inférence de préfixe, pas comme une divulgation arbitraire du prompt.[1]