FlawOpen

flawopen.com/Teardowns/cve-2026-90970-gitlab-ai-gateway-prompt-template-sandbox-escape

● CVE-2026-90970 · CVSS 9.9[4] · Critique
Recherche · FlawOpen

CVE-2026-90970 : évasion du sandbox de prompts de GitLab AI Gateway

Un flux Duo Agent Platform spécialement conçu pouvait sortir du sandbox des modèles de prompt d’AI Gateway et exécuter des commandes. Les opérateurs d’une passerelle auto-hébergée doivent installer la version corrigée de leur branche GitLab.[1], [4]

Vecteur CVSSCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H[4]
💡 Explication en Langage Simple (ELI5)

Imaginez une personne qui remplit un rapport à partir de l’historique d’une conversation. Elle doit copier les champs autorisés comme du texte. Un modèle spécialement conçu pouvait lui faire utiliser la commande « restaurer » du dossier, ce qui amenait l’application à reconstruire des objets actifs depuis les données stockées au lieu de simplement copier des mots. La frontière entre afficher des données et demander à l’application de les interpréter était alors franchie. Le rapport du chercheur décrit le chemin précis avec parse_raw() et pickle ; l’avis de GitLab confirme la sortie du sandbox et l’exécution de commandes, sans détailler toutes les étapes.[3], [6]

Concepts Clés et Termes

AI Gateway
Service GitLab reliant les fonctions d’IA aux fournisseurs de modèles ; les installations auto-hébergées ont une image Gateway versionnée séparément.[5], [1]
Prompt template
Modèle combinant des instructions fixes avec des valeurs d’exécution, comme les entrées de flux et le contexte de conversation.[3], [6]
Sandbox escape
Utilisation d’un comportement d’un environnement restreint pour franchir la limite qu’il devait imposer.[3], [6]
CWE-1336
Neutralisation incorrecte d’éléments spéciaux d’un moteur de modèles ; l’enregistrement CVE attribue cette faiblesse.[4], [1]

Correspondances de référentiels

  • Référence CWE pour la faiblesse décrite dans ce guide : CWE-1336

Analyse de Cause Racine

GitLab confirme que, dans certaines conditions, un utilisateur authentifié ayant accès à Duo Agent Platform pouvait utiliser une configuration spéciale pour sortir du sandbox des modèles de prompt et exécuter des commandes arbitraires sur AI Gateway. Le problème est classé CWE-1336, CVSS 9,9. L’avis ne précise pas toutes les conditions ni la fonction vulnérable. Un commit public du 17/09/2026 ajoute PromptSandboxedEnvironment, décrit comme empêchant l’accès aux méthodes d’objets liés, et renforce les opérations et filtres Jinja. Cela concorde avec la restriction des actions des modèles sur les objets d’exécution, mais le commit ne cite pas ce CVE et les sources examinées ne le relient pas aux images corrigées. Présentez-le comme un renforcement public distinct ; aucune source publique ne le relie à ce CVE ni aux images corrigées. L’élément public GitLab nº 1850 décrit un chemin plus précis : un modèle Jinja appelle parse_raw() sur un HumanMessage de LangChain présent dans l’historique de conversation, ce qui déclenche la désérialisation pickle. L’élément attribue cette démonstration au rapport du chercheur et indique qu’elle s’est produite dans l’environnement de staging de GitLab ; l’avis du fournisseur ne détaille pas toutes les conditions concernées.[1], [3], [4], [2]

Versions corrigées de l’AI Gateway auto-hébergé

Déploiement / brancheVersion corrigée de l’AI GatewayAction de l’opérateur
GitLab auto-hébergé, branche 19.219.2.4Passez au tag correspondant de l’image Gateway auto-hébergée et vérifiez l’image déployée.
GitLab auto-hébergé, branche 19.319.3.2Passez au tag correspondant de l’image Gateway auto-hébergée et vérifiez l’image déployée.
GitLab auto-hébergé, branche 19.419.4.1Passez au tag correspondant de l’image Gateway auto-hébergée et vérifiez l’image déployée.
AI Gateway hébergé par GitLabGitLab indique qu’il était déjà corrigé.Vérifiez que le déploiement utilise le Gateway hébergé par GitLab ; les consignes d’image auto-hébergée ne s’appliquent pas ici.

Déroulement de l'Attaque Étape par Étape

Étape 1

Un utilisateur peut configurer un flux personnalisé

L’avis exige un utilisateur authentifié ayant accès à Duo Agent Platform. Il ne précise ni rôle particulier ni toutes les capacités nécessaires.[1], [3]

Étape 2

Le flux fournit une configuration de modèle spéciale

Une configuration spéciale atteint le sandbox des modèles de prompt. L’avis public ne fournit ni reproduction sûre complète ni expression exacte.[1], [3]

Étape 3

Le modèle franchit la limite du sandbox

L’élément public GitLab nº 1850 indique qu’un modèle pouvait appeler parse_raw() sur un HumanMessage de LangChain de l’historique de conversation et déclencher la désérialisation pickle. C’est le chemin documenté par le chercheur ; ne supposez pas qu’il couvre tous les modèles concernés ni qu’il correspond au correctif du fournisseur.[3]

Étape 4

Des commandes peuvent s’exécuter sur la passerelle

L’impact confirmé est l’exécution de commandes arbitraires sur AI Gateway, côté serveur, pas un simple défaut du modèle à suivre des instructions.[1], [3]

Code Source : Vulnérable vs Sécurisé

L’élément GitLab 1850 indique que le flux vulnérable exposait un objet HumanMessage de LangChain à Jinja, où un modèle pouvait appeler parse_raw() et déclencher la désérialisation pickle. Cet exemple défensif garde les objets de message hors du contexte du modèle, ne transmet que du texte et refuse l’accès aux attributs appelables et aux méthodes. Il illustre la frontière à fermer ; ce n’est pas le correctif publié par GitLab. Le commit public 89b72644 constitue un changement de sécurité distinct : son titre porte sur les écritures Jinja block-set, et aucune source publique ne le relie à CVE-2026-90970 ni aux images corrigées. Les opérateurs doivent suivre les versions corrigées indiquées par le fournisseur.[3], [2], [6]

Extrait source: ai_gateway/prompts/base.py (formateur vulnérable signalé ; défense illustrative)

IMPLÉMENTATION VULNÉRABLE
# Extrait du chemin vulnérable signalé dans l’élément de travail GitLab 1850.
from jinja2 import PackageLoader
from jinja2.sandbox import SandboxedEnvironment
from langchain_core.prompts.string import DEFAULT_FORMATTER_MAPPING
from typing import Any

jinja_loader = PackageLoader("ai_gateway.prompts", "definitions")
jinja_env = SandboxedEnvironment(loader=jinja_loader)

def jinja2_formatter(template: str, /, **kwargs: Any) -> str:
    return jinja_env.from_string(template).render(**kwargs)

DEFAULT_FORMATTER_MAPPING["jinja2"] = jinja2_formatter
PATCH SÉCURISÉ ET ROBUSTE
# Modèle de défense illustratif ; ce n’est pas le correctif de GitLab pour cette CVE.
from typing import Any
from jinja2 import StrictUndefined
from jinja2.sandbox import ImmutableSandboxedEnvironment

class DataOnlyPromptEnvironment(ImmutableSandboxedEnvironment):
    def is_safe_attribute(self, obj: Any, attr: str, value: Any) -> bool:
        if callable(value):
            return False
        return super().is_safe_attribute(obj, attr, value)

    def is_safe_callable(self, obj: Any) -> bool:
        return False

def render_prompt(template: str, history: list[Any]) -> str:
    safe_history = []
    for message in history:
        content = message.content
        if not isinstance(content, str):
            raise TypeError("prompt history must contain text only")
        safe_history.append({"content": content})

    env = DataOnlyPromptEnvironment(undefined=StrictUndefined)
    return env.from_string(template).render(history=safe_history)

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources