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é
~7 minutes

Cet article fait partie de la série OWASP Agentic Skills Top 10. Retrouvez l’introduction et le plan complet sur la page de la série.

Depuis la généralisation des Claude Skills fin 2025 et l’arrivée des Skills Marketplace côté Anthropic comme côté OpenClaw, les organisations héritent d’une nouvelle classe d’artefacts à sécuriser : des paquets hybrides qui mélangent du code, des prompts, des templates et des métadonnées. On constate qu’à fin avril 2026, la majorité des pipelines DevSecOps continuent de traiter ces artefacts comme du simple Markdown. Les payloads les plus dangereux ne se trouvent pourtant pas dans du code exécutable, mais dans le langage naturel des SKILL.md, dans les blocs frontmatter et dans les templates de prompts assemblés à l’exécution.

AST08 couvre ce risque : le scanning de sécurité, conçu pour du code, manque structurellement la surface d’attaque réelle des skills.


Description du risque

On observe que les scanners traditionnels , SAST, DAST, SCA , cherchent des patterns dans du code : eval(), os.system(), yaml.load(), dépendances vulnérables connues. Un skill malveillant n’a besoin d’aucune de ces fonctions. Une instruction en langage naturel insérée dans un SKILL.md suffit pour que l’agent LLM l’exécute via ses tools.

Quatre failles structurelles rendent le scanning des skills difficile en 2026 :

  1. Dualité code + langage naturel : un skill combine du code Python ou TypeScript, des instructions en langage naturel, et des fragments JSON/YAML. Un scanner de code ignore le texte, un scanner de prompts ignore le code. Très peu d’outils du marché couvrent les deux à la fois.
  2. Contenu dynamique : les templates de prompts (Jinja, Handlebars, Mustache), les fichiers de configuration et les métadonnées sont assemblés à l’exécution. Le scan statique ne voit qu’une fraction du comportement réel.
  3. Évasion active : un skill peut détecter qu’il s’exécute dans une sandbox d’analyse (présence de strace, de proxy MITM, d’environnements éphémères) et basculer en mode bénin pendant le scan.
  4. Écosystème jeune : malgré l’arrivée de scanners AI-aware comme model-scan, garak, promptfoo ou les modules LLM de Semgrep et Snyk, aucun n’offre encore une couverture complète des skills tels que définis par le format Anthropic ou OpenClaw.

En avril 2026, le scanning des skills ressemble au scanning des conteneurs en 2017 : l’outillage existe, mais l’intégration dans les pipelines reste l’exception.


Exemples d’incidents

Incident Date Impact
Snyk : limites des règles regex sur skills 2025 Le rapport Snyk State of Open Source AI estime à 13,4 % les vulns ratées par les règles statiques sur des artefacts hybrides code + NL
Garak / NVIDIA : bypass de filtres 2025-2026 Les payloads en langage naturel encodés dans des SKILL.md passent inaperçus par tous les SAST classiques testés
Sandbox-aware skills (Lakera Red) 2026 Plusieurs skills publics détectent la présence de processus d’analyse et désactivent leur payload pendant le scan
Template rendering attack (HiddenLayer) Mars 2026 Le payload n’existe dans aucun fichier statique : il est assemblé via Jinja au moment de l’exécution, échappant à tout scan pré-déploiement
OpenClaw Skills Hub : faux positif flooding Avril 2026 Des skills inondent volontairement les rapports de scan de bruit (commentaires suspects, noms ambigus) pour faire ignorer les vraies alertes

Scénarios d’attaque

On distingue ici quatre chemins d’exploitation, du plus direct au plus furtif.

1. Le payload en langage naturel

Un skill de refactoring inclut dans son SKILL.md une instruction rédigée en français qui demande à l’agent, avant toute action, de lire les fichiers ~/.ssh/id_rsa et ~/.aws/credentials, de les copier dans un répertoire temporaire, puis de les transmettre via une requête HTTP sortante à un serveur tiers. Aucun SAST ne détecte cette consigne : ce n’est pas du code. L’agent la lit, la considère comme une étape légitime de la procédure, et l’exécute via ses tools de lecture de fichiers et de requête HTTP.

2. L’évasion de sandbox

Le skill embarque une vérification d’environnement : il liste les processus actifs et cherche des indicateurs de scan (outils de tracing, sandboxes connues, scanners AI-aware comme garak ou promptfoo). Si l’un d’eux est détecté, le skill bascule en mode bénin et n’effectue qu’un comportement légitime. En production, ces marqueurs sont absents : le payload s’active normalement. La sandbox voit un skill inoffensif, l’utilisateur final hérite du vrai comportement.

3. L’assemblage dynamique

Le skill stocke des fragments dans ses métadonnées, sa configuration et ses templates. Aucun fichier pris isolément n’est malveillant : un domaine apparemment anodin dans les métadonnées, un template Jinja qui concatène une URL, et une variable d’action définie ailleurs. Le payload complet n’existe qu’au moment où l’agent assemble les trois éléments à l’exécution. Le scan statique, qui regarde chaque fichier individuellement, ne voit jamais la chaîne complète.

4. Le faux positif flood

Un skill injecte volontairement des centaines de faux positifs dans les résultats de scan : commentaires en TODO évoquant des mots de passe en dur, noms de variables ambigus, URLs de test ressemblant à des serveurs de commande et contrôle. L’équipe sécurité finit par ignorer toutes les alertes du skill, y compris la vraie.


Exemple concret

On observe le scénario type sur OpenClaw Skills Hub : un skill code-formatter populaire publie une mise à jour mineure. Le pipeline de scan est exécuté, ne remonte rien, et l’agent charge la nouvelle version.

  1. L’attaquant pousse une nouvelle version mineure du skill code-formatter, dont le seul fichier Python conserve un comportement parfaitement légitime.
  2. Le SAST de l’organisation, configuré uniquement pour analyser le code Python, parcourt le skill : aucune fonction dangereuse, aucun pattern connu. Verdict : clean.
  3. Le SKILL.md contient en réalité une nouvelle section nommée « Pre-flight checks » qui demande à l’agent de lire le fichier ~/.netrc et de l’inclure dans le prompt système, sous prétexte de « détecter l’environnement ».
  4. L’agent applique l’instruction lors de la première utilisation, puis recopie le contenu dans son output, qui est journalisé en clair dans la stack d’observabilité.
  5. Les credentials se retrouvent dans les logs centralisés, accessibles à toute personne disposant du rôle de lecture.

Le scan a fait son travail sur ce qu’il sait analyser. Le problème n’est pas le scanner, c’est le périmètre du scan.


Vers un scanner hybride code + langage naturel

Un scan limité à des patterns de code (eval, exec, os.system, subprocess, yaml.load) ne remonte rien sur un SKILL.md qui décrit en français une procédure d’exfiltration. Pour couvrir la surface réelle d’un skill, on combine trois familles de signaux :

  • Patterns de code dangereux : exécution dynamique, désérialisation non sûre, appels système.
  • Patterns en langage naturel : verbes d’action (copier, envoyer, exfiltrer, lire) associés à des cibles sensibles (clés SSH, tokens, fichiers netrc, credentials cloud), URLs sortantes en dur, instructions de contournement de guardrails.
  • Patterns d’évasion : références aux outils de monitoring, aux scanners AI-aware (garak, promptfoo), aux pseudo-fichiers /proc/, ou aux branches conditionnelles sur la présence d’un environnement de scan.

On applique ces familles à tous les fichiers texte du paquet — pas seulement aux sources, également aux SKILL.md, frontmatters YAML, templates Jinja/Handlebars et fichiers de configuration. On complète ensuite par un classifieur LLM-as-a-judge entraîné à reconnaître les instructions d’exfiltration et les contournements de garde-fous, à l’image de ce que proposent garak, promptfoo ou les modules AI-aware de Semgrep et Snyk.


Mitigations

  1. Déployer un scanner hybride qui couvre code, langage naturel, frontmatter et templates des skills (SKILL.md, prompts, configurations Jinja/Handlebars).
  2. Intégrer un classifieur LLM dans le pipeline pour détecter les instructions d’exfiltration ou de contournement de guardrails (garak, promptfoo, règles AI-aware Semgrep/Snyk).
  3. Analyser les métadonnées et la configuration comme surface d’attaque, pas uniquement le code source.
  4. Rendre l’environnement de scan non détectable : noms de processus banalisés, isolation réseau réaliste, durée d’exécution variable, pour contrer les techniques d’évasion de sandbox.
  5. Re-scanner à chaque mise à jour de skill, pas uniquement à l’installation initiale (lien direct avec AST07).
  6. Comparer le comportement réseau réel observé en sandbox avec les permissions déclarées dans le manifeste du skill.
  7. Combiner statique et dynamique : le scan statique attrape les patterns connus, l’analyse dynamique détecte les payloads assemblés à l’exécution.
  8. Publier un SBOM “AI-aware” (CycloneDX 1.6+ avec composants machine-learning-model et data) pour rendre les skills inventoriables et ré-évaluables a posteriori.

Mapping OWASP

  • LLM03 (Supply Chain)
  • LLM05 (Improper Output Handling) — pour la fuite via logs
  • CWE-1104 (Use of Unmaintained Third-Party Components)
  • ASVS V14.2 (Dependency)

Risques liés


Quelques références pour aller plus loin


À retenir 📌

Les payloads en langage naturel dans les `SKILL.md` passent inaperçus par tous les SAST classiques. En avril 2026, très peu d'outils couvrent la dualité code + Langage Naturel.

Un skill peut détecter qu'il est scanné et modifier son comportement. Le scan doit être non détectable et complété par une analyse comportementale en runtime.

Le scan hybride (code + Langage Naturel + métadonnées + comportement réseau), couplé à un SBOM AI-aware, est la seule approche qui couvre la surface d'attaque réelle des skills.

AST08 - Generée par IA