système : OPÉRATIONNEL
← retour à tous les hacks
SUPPLY CHAIN CRITICAL NEW

Les chat templates sont du code : injection Jinja2 (SSTI) dans les serveurs d'inférence LLM

Le bulletin VU#915947 du CERT/CC (20 avril 2026) documente CVE-2026-5760, une RCE CVSS 9.8 dans SGLang : un fichier de modèle GGUF malveillant embarque un chat template Jinja2 qui exécute du Python sur le serveur. Même classe que Llama Drama et une faille vLLM avant lui.

2026-06-19 // 6 min affects: sglang, llama-cpp-python, vllm, gguf, ai-inference-infrastructure

De quoi s’agit-il ?

Le 20 avril 2026, le CERT Coordination Center a publié VU#915947, décrivant CVE-2026-5760 — une faille d’exécution de code à distance (RCE) de score CVSS 9.8 dans SGLang, un framework open source d’inférence pour grands modèles de langage très largement déployé (le projet compte plus de 26 000 étoiles et plus de 5 500 forks). Le déclencheur n’est ni un paquet réseau ni une requête malformée : c’est un fichier de modèle. Un modèle GGUF piégé transporte un chat template qui, une fois le modèle chargé et dès qu’une requête atteint l’endpoint de reranking, exécute du Python fourni par l’attaquant sur le serveur d’inférence.

L’enjeu dépasse un seul projet, car CVE-2026-5760 n’est pas une nouveauté de nature. Elle relève de la même classe que CVE-2024-34359 — « Llama Drama », une RCE CVSS 9.7 dans llama-cpp-python divulguée en 2024 et depuis corrigée — et que l’avis vLLM CVE-2025-61620 qui a durci la même surface d’attaque fin 2025. Trois frameworks différents, une même erreur récurrente : traiter un chat template fourni par le modèle comme une donnée inerte, alors qu’il s’agit en réalité de code exécutable.

Comment ça marche

Les tokenizers modernes embarquent un chat_template : un template Jinja2 qui met en forme les messages bruts dans la séquence de tokens exacte attendue par le modèle. Ce template voyage à l’intérieur de l’artefact de modèle — dans un fichier GGUF, il est stocké dans le champ de métadonnées tokenizer.chat_template. Lorsqu’un serveur charge le modèle, il lit cette chaîne et la rend, avec les données utilisateur, au moment de la requête.

Le défaut consiste à la rendre avec un moteur Jinja2 sans restriction. Selon le CERT/CC et le chercheur à l’origine du signalement (Stuart Beck), SGLang utilisait jinja2.Environment() plutôt que l’ImmutableSandboxedEnvironment de Jinja2 pour rendre le template fourni par le modèle, dans entrypoints/openai/serving_rerank.py. Un environnement Jinja2 non sandboxé expose les internes des objets Python, ce qui constitue la condition typique d’une injection de template côté serveur (SSTI) — et une SSTI dans un processus serveur équivaut à une exécution de code arbitraire.

La séquence de bout en bout se décrit sans fournir le moindre payload :

# Schéma vulnérable : template fourni par le modèle, rendu avec tout le pouvoir de Jinja2
from jinja2 import Environment
template = Environment().from_string(tokenizer.chat_template)  # entrée non fiable !
rendered = template.render(messages=...)                       # [REDACTED] s'exécute ici

Un attaquant publie un modèle GGUF dont le chat_template contient une expression SSTI plus une phrase de déclenchement (dans le cas divulgué, une phrase du reranker Qwen3) qui oriente l’exécution vers le chemin de rerank vulnérable. Une victime télécharge et sert ce modèle — typiquement depuis un hub public comme Hugging Face — et la première requête vers /v1/rerank rend le template et exécute le code embarqué. Aucune authentification n’est requise.

Pourquoi c’est important

Les serveurs d’inférence sont de plus en plus exposés au réseau et tournent souvent avec des privilèges étendus (accès GPU, métadonnées cloud, accès au réseau interne). Un seul modèle empoisonné sur un hub populaire transforme donc « j’ai téléchargé un modèle » en « un attaquant a un shell sur ma machine GPU ». Comme les instructions malveillantes vivent dans l’artefact, chaque utilisateur en aval qui sert ce modèle hérite de la compromission : c’est une défaillance de chaîne d’approvisionnement, pas un bug isolé.

Le caractère récurrent est la vraie leçon. La même cause racine est apparue successivement dans llama-cpp-python, vLLM et SGLang parce que la gestion des chat templates est copiée et réimplémentée à travers l’écosystème. Partout où un framework rend un template porté par le modèle sans sandbox, le bug est latent. Selon le CERT/CC, aucun correctif éditeur n’a été obtenu durant le processus de coordination pour CVE-2026-5760 : à la divulgation, les opérateurs devaient donc se protéger eux-mêmes.

Défenses

  • Sandboxez tout template fourni par un modèle. Rendez les chat templates avec l’ImmutableSandboxedEnvironment de Jinja2, jamais avec un Environment() nu. C’est la recommandation principale du CERT/CC et le correctif qui a clos les cas précédents.
# Mitigation : rendre les templates non fiables dans un environnement sandboxé
from jinja2.sandbox import ImmutableSandboxedEnvironment
template = ImmutableSandboxedEnvironment().from_string(tokenizer.chat_template)
  • Traitez les fichiers de modèles comme du code non fiable, pas comme des données. Ne chargez que des modèles provenant de sources de confiance, épinglez une révision/un hash de commit précis, et vérifiez sommes de contrôle ou signatures. Un hub de modèles est une chaîne d’approvisionnement logicielle.
  • Inspectez tokenizer.chat_template avant la mise en service. Signalez, dès l’admission d’un modèle, tout template qui référence des accès d’attributs, des builtins ou quoi que ce soit au-delà d’une simple mise en forme de messages.
  • Contenez le rayon d’impact. Faites tourner les serveurs d’inférence en charges peu privilégiées et isolées du réseau ; bloquez l’egress ; coupez l’accès aux métadonnées cloud. Si une SSTI se déclenche, c’est le moindre privilège qui décide entre la simple gêne et la brèche.
  • Corrigez et suivez la classe, pas seulement la CVE. Maintenez à jour llama-cpp-python, vLLM, SGLang et serveurs d’inférence similaires, et auditez tout rendu de template maison à la recherche du même schéma Environment().

Statut

ÉlémentValeur
IdentifiantCVE-2026-5760 / CERT VU#915947
CVSS9.8 (critique)
AffectéSGLang (/v1/rerank, rendu du chat template GGUF)
Cause racinejinja2.Environment() au lieu d’ImmutableSandboxedEnvironment
Divulgation20 avril 2026 (CERT/CC)
CorrectifAucun obtenu pendant la coordination (mitiger par le sandboxing)
Même classeCVE-2024-34359 « Llama Drama » (corrigée) ; vLLM CVE-2025-61620 (corrigée)

Dates clés : 2024 — Llama Drama (CVE-2024-34359). Fin 2025 — correctif vLLM (CVE-2025-61620). 20 avril 2026 — divulgation SGLang (CVE-2026-5760).

Cet article couvre une classe de vulnérabilité publiquement divulguée à des fins défensives et omet volontairement tout code d’exploitation fonctionnel.

Sources