flawopen.com/AI Security/semantic-kernel-inmemory-vector-filter-cve-2026-26030
Semantic Kernel CVE-2026-26030 : injection de filtre
Des filtres vectoriels influencés par des prompts contournaient les contrôles AST jusqu’à eval en Python. Passez à la version 1.39.4 ou ultérieure.[1], [4]
Imaginez une bibliothécaire qui reçoit une fiche de recherche, vérifie quelques mots connus, puis demande à l’ordinateur d’exécuter toute la fiche comme un petit programme. Une personne capable de modifier la fiche peut exploiter les lacunes du contrôle. CVE-2026-26030 permettait à un filtre influencé par le modèle de passer de la recherche de données à l’exécution de code dans le processus applicatif.[4]
Concepts Clés et Termes
InMemoryVectorStore- Stockage vectoriel dans le processus Semantic Kernel ; la recherche peut filtrer les enregistrements avant de renvoyer les résultats.[4]
Expression de filtre- Prédicat qui décide si un enregistrement figure dans les résultats vectoriels. Dans le chemin Python concerné, les filtres textuels étaient analysés puis évalués comme des expressions Python.[4]
Arbre syntaxique abstrait (AST)- Représentation structurée du code analysé. Une validation incomplète peut laisser des voies vers l’introspection des objets Python.[2], [4]
Injection de prompt- Un texte non fiable influence le modèle pour lui faire effectuer une action imprévue. Le risque dépend de l’exposition du filtre vulnérable aux entrées pilotées par le modèle.[4]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-94
Déroulement de l'Attaque Étape par Étape
Un contenu non fiable influence le filtre choisi par le modèle
L’attaque nécessite une injection de prompt qui influence l’entrée de l’agent et un Search Plugin utilisant le filtre InMemoryVectorStore par défaut. Une expression générée par le modèle n’est pas fiable simplement parce qu’elle passe par un appel typé.[4]
Le filtre devient du code Python
Avant le correctif, le filtre était analysé en AST puis évalué sans builtins, mais les contrôles ne limitaient pas assez la traversée du modèle objet. La version 1.39.4 conserve ast.Subscript pour accéder aux champs des enregistrements et combine une liste de nœuds autorisés, le blocage des attributs dangereux, les noms isolés limités aux paramètres lambda et une liste d’appels autorisés. Le code upstream ne rejette pas les indices à eux seuls ; eval sans builtins n’est pas un sandbox.[2], [4]
L’exécution atteint le processus applicatif
Si l’expression contourne la validation pendant la recherche, une logique influencée par l’attaquant peut s’exécuter dans le processus Python de l’agent. La portée dépend des permissions, de l’environnement, des identifiants et du réseau de ce processus.[4]
Corrigez la frontière du parseur et recherchez une exposition
Passez semantic-kernel en 1.39.4 ou ultérieur. Le correctif vérifie les attributs dangereux et limite les noms nus au paramètre lambda ; l’analyse documente aussi des listes autorisées AST et de fonctions. Recherchez les anomalies de processus, réseau et identifiants pendant la période concernée.[1], [2], [3], [4]
Code Source : Vulnérable vs Sécurisé
Le volet vulnérable présente un schéma représentatif, pas le code exact du produit : une valeur contrôlée par le modèle est interpolée dans la syntaxe Python avant eval. Le volet corrigé met en évidence les contrôles upstream de 1.39.4. La liste AST configurée inclut ast.Subscript pour accéder aux champs des enregistrements ; le correctif la combine au blocage des attributs dangereux, aux noms isolés limités à lambda et aux appels autorisés. Retirer les builtins ne transforme pas eval en sandbox. Mettez le paquet à jour ; ne copiez pas cet extrait comme validateur autonome.[2], [6], [4]
# Schéma représentatif avant correctif : le texte du modèle modifie l’expression.
filter_text = f"lambda x: x.city == '{model_selected_city}'"
tree = ast.parse(filter_text, mode="eval")
code = compile(tree, filename="<filter>", mode="eval")
filter_fn = eval(code, {"__builtins__": {}}, {}) # nosec
def validate_filter(self, tree):
# Contrôles upstream ciblés de 1.39.4 ; messages d’exception abrégés.
# Les filtres de premier niveau doivent être des expressions lambda.
if not (isinstance(tree, ast.Expression) and isinstance(tree.body, ast.Lambda)):
raise VectorStoreOperationException("Filter must be a lambda expression")
lambda_param_names = {arg.arg for arg in tree.body.args.args}
for node in ast.walk(tree):
if type(node) not in self.allowed_filter_ast_nodes:
raise VectorStoreOperationException("AST node type is not allowed")
if isinstance(node, ast.Attribute) and node.attr in self.blocked_filter_attributes:
raise VectorStoreOperationException("Dangerous attribute is not allowed")
if isinstance(node, ast.Name) and node.id not in lambda_param_names:
raise VectorStoreOperationException("Only lambda parameter names are allowed")
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
func_name = node.func.attr
else:
func_name = None
if func_name and func_name not in self.allowed_filter_functions:
raise VectorStoreOperationException("Function is not allowed")
code = compile(tree, filename="<filter>", mode="eval")
func = eval(code, {"__builtins__": {}}, {}) # nosec
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Recensez les environnements Python déployés avec semantic-kernel et consignez la version résolue, y compris conteneurs, notebooks, workers et images CI.[4]
- ✓Mettez à jour les installations concernées vers 1.39.4 ou ultérieur, régénérez les fichiers de verrouillage et reconstruisez les images avec les dépendances corrigées.[1], [3]
- ✓Avant la mise à jour, désactivez en production le filtre InMemoryVectorStore ; le fournisseur recommande de ne pas utiliser ce stockage en production comme mesure temporaire.[1]
- ✓Pour chaque PR qui ajoute ou modifie un filtre de recherche vectorielle, ajoutez des tests prouvant que le texte utilisateur, les contenus récupérés et les arguments du modèle ne peuvent pas modifier la structure analysée du filtre ; rejetez les formes non prises en charge.[4]
- ✓Ajoutez un test qui rejette les attributs AST bloqués et les noms autres que le paramètre lambda, sans payload exécutable.[2]
- ✓Représentez les filtres choisis par le modèle sous forme de données typées, avec des listes explicites de champs et d’opérateurs autorisés ; ajoutez des tests qui rejettent les chaînes d’expression et les champs ou opérateurs inconnus.
- ✓Limitez l’accès des agents aux fichiers, au réseau et aux identifiants pour réduire l’impact d’une faille future.[4]
- ✓Ajoutez un test de régression de détection sur l’hôte de l’agent qui génère des événements représentatifs de processus enfants et de connexions sortantes, puis vérifiez que la règle de télémétrie déployée déclenche une alerte pour les deux.[4]