
Advisory and proof-of-concept for OS command injection in an MCP ffmpeg helper, with root-cause analysis, detector guidance, and mitigations for an unfixed npm package.
runFFmpegCommand passes a string built from MCP tool parameters to
spawn(…, { shell: true }), so shell metacharacters in options, format,
codec, pixelFormat or extraOptions execute on the host running the server.
⚠ No fixed version exists
As of 2026-08-22 the latest npm release is 0.2.1, which is the affected version, and the sink is still present on the repository's default branch. There is nothing to upgrade to. See Mitigation for what users can do in the meantime.
MCP tool arguments are attacker-influenced. They are produced by an LLM from
the content the agent processes — documents, web pages, tool output — so
indirect prompt injection puts an attacker in control of the values a tool
handler receives. A handler that feeds any of those values into a shell turns
that influence into code execution on the host running the MCP server. This is
the same model as the precedent case aws-mcp-server / CVE-2026-5058.
A media-conversion server is a particularly natural target for this: the files it is asked to process are exactly the untrusted content an agent picks up from elsewhere.
src/utils/ffmpeg.ts — the command is assembled as a string and the shell is
enabled explicitly:
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 }) runs the whole string through
/bin/sh -c. Everything the caller contributed to command is shell syntax.
src/tools/handlers.ts reaches that helper from three tools, interpolating
parameters that are never validated for content:
| Tool | Injectable parameters |
|---|---|
convert_video |
Only inputPath and outputPath are checked at all, and the Zod schemas
validate type (z.string()), never content — so metacharacters pass straight
through.
A convert_video call with options set to ; touch /tmp/marker ; produces:
/bin/sh -c "ffmpeg -i /tmp/in.mp4 ; touch /tmp/marker ; /tmp/out.mp4"
poc/driver.mjs speaks MCP over stdio: it initializes the
server, issues one tools/call for convert_video with an injected options
value, and checks for a marker file. The payload is a benign touch.
npm pack @sworddut/[email protected]
tar xf sworddut-mcp-ffmpeg-helper-0.2.1.tgz && cd package && npm install
node ../poc/driver.mjs
Expected on an affected version:
==== POC RESULT (@sworddut/mcp-ffmpeg-helper) ====
marker /tmp/PWNED_ffmpeg created: true
verdict: CONFIRMED — command injection executed
ffmpeg does not need to be installed — the injected command runs in the same
/bin/sh -c string regardless of whether the leading binary resolves.
Validated 2026-06-01 in an isolated container, re-validated 2026-06-13, and the
sink re-confirmed present on the default branch on 2026-08-22. Run it only
against infrastructure you are authorized to test.
ffmpeg command line containing ;, &&, ||, |, a backtick or $(.
Legitimate ffmpeg invocations from this server do not.sh -c process whose command line starts with ffmpeg and contains a
second command after a separator — a process-tree signal (auditd, eBPF,
Falco, EDR) that does not depend on application logging.ffmpeg and ffprobe are
expected; a shell, an interpreter or a network client is not.Running FFmpeg command: ffmpeg <command>, which is the cheapest place to
spot an injected value if the logs are collected at all.options, format, , or
contain shell metacharacters.MITRE ATT&CK T1059.004 Command and Scripting Interpreter: Unix Shell.
No patched release exists, so the options are containment and removal:
runFFmpegCommand.The upstream fix is to drop shell: true and pass ffmpeg an argument
vector — spawn("ffmpeg", ["-i", inputPath, …]) — building that array from
allowlisted format, codec and pixelFormat values rather than from
free-form strings. Content validation, not just Zod type validation, is the
underlying requirement.
NIST SP 800-53r5 SI-10; OWASP ASVS v4 §5.3.8; CWE-78 mitigations M1 and M2.
aws-mcp-server)Companion advisories from the same research pass: CVE-2026-52616, CVE-2026-52618.
MIT — see LICENSE.
| CVE | CVE-2026-52617 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Package | @sworddut/mcp-ffmpeg-helper (npm) |
| Affected | 0.2.1 and earlier — all published versions |
| Fixed in | none |
| CVSS v3.1 | AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 7.8 High (stdio transport). AV:N → 9.8 Critical if the server is wrapped in a network transport. |
| Reported by | s1ko (github.com/s1ko, [email protected]) |
| CVE assigned | 2026-07-13, MITRE CNA-LR |
optionsextract_audio | format |
create_video_from_images | codec, pixelFormat, extraOptions |
codecpixelFormatextraOptions| Date | Event |
|---|
| 2026-05-29 | Vulnerability identified by source review of the published tarball |
| 2026-06-01 | Dynamically validated over MCP stdio in an isolated container |
| 2026-06-13 | Re-validated on a second host; the repository has no Private Vulnerability Reporting enabled, and the package.json repository/bugs/homepage fields are left at the github.com/yourusername template, leaving no documented security contact |
| 2026-07-13 | MITRE CNA-LR assigns CVE-2026-52617, s1ko credited as discoverer |
| 2026-08-22 | Sink re-confirmed on the default branch; npm latest still 0.2.1; this write-up published |