
Avis de sécurité et preuve de concept pour une injection de commandes OS dans un assistant MCP ffmpeg, avec analyse des causes profondes, conseils de détection et mesures d'atténuation pour un paquet npm non corrigé.
runFFmpegCommand transmet une chaîne construite à partir des paramètres de l'outil MCP à
spawn(…, { shell: true }), de sorte que les métacaractères du shell présents dans options, format,
codec, pixelFormat ou extraOptions s'exécutent sur l'hôte exécutant le serveur.
⚠ Aucune version corrigée n'existe
Au 2026-08-22, la dernière version publiée sur npm est la 0.2.1, qui est la version affectée, et le point d'injection est toujours présent sur la branche par défaut du dépôt. Il n'y a rien vers quoi effectuer une mise à niveau. Voir Atténuation pour ce que les utilisateurs peuvent faire en attendant.
| CVE | CVE-2026-52617 |
| CWE | CWE-78 (Neutralisation incorrecte des éléments spéciaux utilisés dans une commande OS) |
| Paquet | @sworddut/mcp-ffmpeg-helper (npm) |
| Affecté | 0.2.1 et antérieures — toutes les versions publiées |
| Corrigé dans | aucune |
| CVSS v3.1 | AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 8.4 Élevé (transport stdio). AV:N → 9.8 Critique si le serveur est encapsulé dans un transport réseau. |
| Signalé par | s1ko (github.com/s1ko, [email protected]) |
| CVE attribué | 2026-07-13, MITRE CNA-LR |
Les arguments des outils MCP sont influencés par l'attaquant. Ils sont produits par un LLM à partir
du contenu que l'agent traite — documents, pages web, sortie d'outils — de sorte qu'une injection de prompt
indirecte place un attaquant aux commandes des valeurs que reçoit un gestionnaire d'outil. Un gestionnaire qui injecte l'une de ces valeurs dans un shell transforme
cette influence en exécution de code sur l'hôte exécutant le serveur MCP. C'est
le même modèle que le cas précédent aws-mcp-server / CVE-2026-5058.
Un serveur de conversion de médias est une cible particulièrement naturelle pour cela : les fichiers qu'on lui demande de traiter sont exactement le contenu non fiable qu'un agent récupère ailleurs.
src/utils/ffmpeg.ts — la commande est assemblée sous forme de chaîne et le shell est
activé explicitement :
function runProcess(command: string, args: string[], useShell = false) {
const child = spawn(command, args, { shell: useShell, windowsHide: true });
…
}
export async function runFFmpegCommand(command: string): Promise<string> {
const { stdout, stderr, code } = await runProcess(`ffmpeg ${command}`, [], true);
…
}
spawn("ffmpeg " + command, [], { shell: true }) exécute la chaîne entière via
/bin/sh -c. Tout ce que l'appelant a contribué à command est de la syntaxe shell.
src/tools/handlers.ts atteint ce helper depuis trois outils, en interpolant
des paramètres qui ne sont jamais validés quant à leur contenu :
| Outil | Paramètres injectables |
|---|---|
convert_video | options |
extract_audio | format |
create_video_from_images | codec, pixelFormat, extraOptions |
Seuls inputPath et outputPath sont vérifiés, et les schémas Zod
valident le type (z.string()), jamais le contenu — les métacaractères passent donc directement.
Un appel convert_video avec options défini à ; touch /tmp/marker ; produit :
/bin/sh -c "ffmpeg -i /tmp/in.mp4 ; touch /tmp/marker ; /tmp/out.mp4"
poc/driver.mjs communique avec MCP via stdio : il initialise le
serveur, émet un tools/call pour convert_video avec une valeur options injectée,
et vérifie la présence d'un fichier marqueur. La charge utile est un touch bénin.
npm pack @sworddut/[email protected]
tar xf sworddut-mcp-ffmpeg-helper-0.2.1.tgz && cd package && npm install
node ../poc/driver.mjs
Attendu sur une version affectée :
==== POC RESULT (@sworddut/mcp-ffmpeg-helper) ====
marker /tmp/PWNED_ffmpeg created: true
verdict: CONFIRMED — command injection executed
ffmpeg n'a pas besoin d'être installé — la commande injectée s'exécute dans la même chaîne /bin/sh -c, que le binaire de tête soit résolu ou non.
Validé le 2026-06-01 dans un conteneur isolé, revalidé le 2026-06-13, et le
sink reconfirmé présent sur la branche par défaut le 2026-08-22. Ne l'exécutez que
contre une infrastructure que vous êtes autorisé à tester.
ffmpeg contenant ;, &&, ||, |, un backtick ou $(.
Les invocations ffmpeg légitimes de ce serveur n'en contiennent pas.sh -c dont la ligne de commande commence par ffmpeg et contient une
seconde commande après un séparateur — un signal d'arbre de processus (auditd, eBPF,
Falco, EDR) qui ne dépend pas de la journalisation applicative.ffmpeg et ffprobe sont
attendus ; un shell, un interpréteur ou un client réseau ne le sont pas.Running FFmpeg command: ffmpeg <command>, ce qui est l'endroit le moins coûteux pour
repérer une valeur injectée si les journaux sont collectés.options, format, codec, pixelFormat ou
extraOptions contiennent des métacaractères de shell.MITRE ATT&CK T1059.004 Command and Scripting Interpreter: Unix Shell.
Aucune version corrigée n'existe, les options sont donc le confinement et la suppression :
runFFmpegCommand.Le correctif amont consiste à supprimer shell: true et à passer à ffmpeg un vecteur
d'arguments — spawn("ffmpeg", ["-i", inputPath, …]) — en construisant ce tableau à partir
de valeurs format, codec et pixelFormat figurant sur une liste autorisée plutôt qu'à
partir de chaînes libres. La validation du contenu, et pas seulement la validation de type
Zod, est l'exigence sous-jacente.
NIST SP 800-53r5 SI-10 ; OWASP ASVS v4 §5.3.8 ; atténuations CWE-78 M1 et M2.
| Date | Événement |
|---|---|
| 2026-05-29 | Vulnérabilité identifiée par revue du code source du tarball publié |
| 2026-06-01 | Validée dynamiquement via MCP stdio dans un conteneur isolé |
| 2026-06-13 | Revalidée sur un second hôte ; le dépôt n'a pas le Private Vulnerability Reporting activé, et les champs repository/bugs/homepage de package.json sont restés au modèle github.com/yourusername, ne laissant aucun contact de sécurité documenté |
| 2026-07-13 | Le CNA-LR du MITRE attribue CVE-2026-52617, s1ko crédité comme découvreur |
| 2026-08-22 | Sink reconfirmé sur la branche par défaut ; npm latest toujours 0.2.1 ; publication de cette analyse |
aws-mcp-server)Avis complémentaires issus de la même passe de recherche : CVE-2026-52616, CVE-2026-52618.