Security musings

Catégories

Tags

🔍 Licence d'Utilisation 🔍

Sauf mention contraire, le contenu de ce blog est sous licence CC BY-NC-ND 4.0.

© 2025 à 2042 Sébastien Gioria. Tous droits réservés.

⏱️
Temps de lecture estimé
~4 minutes

L’injection de commandes, ça date de l’ère Unix. Mais dans le contexte MCP, c’est pire : le modèle IA est l’interpréteur. Et lui, il ne doute jamais de ce qu’il construit.

Partie de la série OWASP MCP Top 10


Description du risque

L’injection de commandes dans le contexte MCP est une version amplifiée du problème classique car ici, le modèle IA est l’interpréteur.

La Command Injection dans le contexte MCP se produit quand un agent IA construit et exécute des commandes système en utilisant des entrées non fiables sans validation appropriée. C’est analogue aux injections classiques (SQL, shell), mais avec une différence clé : le modèle IA est l’interpréteur, et le texte en langage naturel est le payload.

L’agent ne distingue pas “instruction de travail” et “commande malveillante” , caril fait confiance à tout ce qui arrive dans son contexte. C’est shell=True appliqué à l’IA.


Vecteurs d’attaque

On distingue quatre vecteurs principaux d’injection de commandes dans les outils MCP :

1. Injection directe via le prompt utilisateur

L’attaquant fournit un prompt contenant des métacaractères shell (;, &&, |, backticks, $(...)). L’agent les passe au système sans validation.

2. Injection indirecte via contenu de fichier

L’agent lit un fichier dont le contenu contient des instructions déguisées en commandes à exécuter.

3. Injection via arguments de commande

Manipulation des arguments : path traversal (../../etc/passwd), options (--help; cat /etc/shadow).

4. Exploitation de shell=True

Le vecteur le plus critique en Python : subprocess.run(cmd, shell=True) active toute l’interpolation shell.


Analyse STRIDE

Catégorie STRIDE Applicable Explication
Spoofing (Usurpation d’identité) Modéré Les commandes injectées se font passer pour des opérations légitimes.
Tampering (Falsification) Oui L’exécution de commandes permet de modifier des fichiers système et des configurations.
Repudiation (Répudiation) Oui Les commandes sont attribuées au processus MCP, pas à l’attaquant.
Information Disclosure (Divulgation d’informations) Oui cat /etc/shadow, env — un simple curl suffit à exfiltrer.
Denial of Service (Déni de service) Oui rm -rf /, fork bombs, kill -9 -1 — des commandes destructrices triviales.
Elevation of Privilege (Élévation de privilèges) Oui (PRIMAIRE) L’injection de commandes = exécution de code avec les privilèges du serveur MCP. Si le processus tourne en root, c’est un accès total.

Impact potentiel

ImpactNiveauDescription de l'impact
ConfidentialitéCritiqueL'attaquant lit tous les fichiers accessibles : clés privées, tokens, données utilisateur. Exfiltration en une commande.
IntégritéCritiqueModification de fichiers, installation de backdoors, altération de logs pour couvrir ses traces.
DisponibilitéÉlevéCommandes destructrices, saturation des ressources, arrêt de services.
RéputationÉlevéUn serveur MCP compromis utilisé pour attaquer d'autres cibles impacte directement l'organisation.

Recommandations de mitigation

Je constate que la majorité des injections de commandes dans les outils MCP sont évitables avec quelques réflexes fondamentaux. Voici les cinq règles que a appliquer systématiquement.

1. Bannir shell=True

shell=True est la source de presque tous les problèmes. Il active l’interpolation shell complète : métacaractères, substitution de variables, pipes. La règle est simple : on passe toujours une liste d’arguments, jamais une chaîne construite dynamiquement.

2. Whitelist stricte des commandes autorisées

Plutôt que de blacklister les commandes dangereuses (une bataille perdue d’avance), définir une liste fermée de ce qui est autorisé. Tout ce qui n’est pas dans la liste est rejeté avant même d’atteindre le système.

Une whitelist de 10 commandes est plus sûre qu’une blacklist de 10 000 patterns.

3. Valider et assainir les arguments avec shlex

Même avec shell=False, un argument malformé peut provoquer des comportements inattendus (path traversal, null bytes). Utilisershlex.split() pour parser les arguments, et vérifier l’absence de .. dans les chemins par exemple.

import shlex, os

def sanitize_path(path: str) -> str:
    clean = os.path.normpath(path)
    if ".." in clean.split(os.sep):
        raise ValueError("Path traversal détecté")
    return clean

4. Exécuter dans un sandbox isolé

L’agent MCP ne doit jamais tourner en root. L’executer dans un conteneur avec un utilisateur dédié non-privilégié, un filesystem en lecture seule, sans accès réseau sortant, et avec un timeout strict sur toutes les exécutions.

# docker-compose.yml
services:
  mcp-agent:
    user: "1000:1000"
    read_only: true
    network_mode: none
    tmpfs:
      - /tmp:size=100m

5. Auditer chaque exécution

Toute commande exécutée par un agent MCP doit être loguée avec son contexte complet : utilisateur, timestamp, commande, arguments, code de retour. Ces logs sont envoyés vers un SIEM et des alertes déclenchées en cas d’accès à des fichiers sensibles (/etc/shadow, .ssh/, variables d’environnement contenant KEY ou SECRET).


Quelques références pour aller plus loin


À retenir

À retenir 📌

  • shell=True est l'ennemi : ne jamais passer une commande construite par un agent à un shell
  • Whitelist, pas blacklist : définir une liste stricte de commandes autorisées, tout le reste est refusé
  • Sandbox obligatoire : exécuter dans unconteneur isolé, sans réseau, lecture seule, utilisateur non-privilégié
  • L'injection indirecte est le vrai danger : un fichier lu par l'agent peut contenir des instructions déguisées
  • Logger tout : chaque commande exécutée doit être auditée
  • Le processus MCP ne doit jamais tourner en root

MCP05