
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 }), si bien que les métacaractères shell présents dans options, format,
codec, pixelFormat ou extraOptions s'exécutent sur l'hôte qui exécute le serveur.
⚠ Aucune version corrigée n'existe
En date du 2026-08-22, la dernière version npm est 0.2.1, qui est la version concernée, et le sink 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 Mitigation pour ce que les utilisateurs peuvent faire en attendant.
Les arguments des outils MCP sont influençables par un attaquant. Ils sont produits par un LLM à partir
du contenu que l'agent traite — documents, pages web, sorties d'outils — si bien que
l'injection indirecte de prompt place un attaquant en contrôle des valeurs qu'un gestionnaire
d'outil reçoit. Un gestionnaire qui transmet l'une de ces valeurs à un shell transforme
cette influence en exécution de code sur l'hôte qui exécute 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 média est une cible particulièrement naturelle : les fichiers qu'on lui demande de traiter sont précisément 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 toute la chaîne via
/bin/sh -c. Tout ce que l'appelant a introduit dans command est de la syntaxe shell.
src/tools/handlers.ts atteint ce gestionnaire depuis trois outils, en interpolant
des paramètres dont le contenu n'est jamais validé :
| Outil | Paramètres injectables |
|---|---|
convert_video |
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 en MCP sur stdio : il initialise le
serveur, émet un appel 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
Résultat attendu sur une version concerné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 en tête soit résolu ou non.
Validé le 2026-06-01 dans un conteneur isolé, re-validé le 2026-06-13, et le
sink re-confirmé présent sur la branche par défaut le 2026-08-22. Ne l'exécutez que
contre des infrastructures que vous êtes autorisé à tester.
ffmpeg contenant ;, &&, ||, |, un accent grave 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 au niveau de l'arborescence des processus (auditd, eBPF,
Falco, EDR) qui ne dépend pas des journaux applicatifs.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 plus simple pour
repérer une valeur injectée, si tant est que les journaux soient collectés.options, , , ou
contiennent des métacaractères 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 le retrait :
runFFmpegCommand.Le correctif en amont consiste à abandonner shell: true et à passer à ffmpeg un vecteur
d'arguments — spawn("ffmpeg", ["-i", inputPath, …]) — en construisant ce tableau à partir de
valeurs format, codec et pixelFormat autorisées (allowlist) 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 ; CWE-78, mitigations M1 et M2.
aws-mcp-server)Avis de sécurité associés issus de la même campagne de recherche : CVE-2026-52616, CVE-2026-52618.
MIT — voir LICENSE.
| CVE | CVE-2026-52617 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Paquet | @sworddut/mcp-ffmpeg-helper (npm) |
| Versions concernées | 0.2.1 et versions antérieures — toutes les versions publiées |
| Corrigée dans | aucune |
| CVSS v3.1 | AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 7.8 Élevé (transport stdio). AV:N → 9.8 Critique si le serveur est exposé via un transport réseau. |
| Signalée par | s1ko (github.com/s1ko, [email protected]) |
| CVE assignée | 2026-07-13, MITRE CNA-LR |
options |
extract_audio | format |
create_video_from_images | codec, pixelFormat, extraOptions |
formatcodecpixelFormatextraOptions| Date | Événement |
|---|
| 2026-05-29 | Vulnérabilité identifiée par revue du code source du tarball publié |
| 2026-06-01 | Validation dynamique sur MCP stdio dans un conteneur isolé |
| 2026-06-13 | Re-validation sur un second hôte ; le dépôt n'a pas activé le signalement privé de vulnérabilités (Private Vulnerability Reporting), et les champs package.json repository/bugs/homepage sont laissés sur le modèle github.com/yourusername, sans aucun contact de sécurité documenté |
| 2026-07-13 | MITRE CNA-LR assigne CVE-2026-52617, s1ko crédité comme découvreur |
| 2026-08-22 | Sink re-confirmé sur la branche par défaut ; la dernière version npm est toujours 0.2.1 ; publication de ce rapport |