Sploitus

Exploit for CVE-2026-52617

githubexploit Β· 2026-08-22

Exploit Code

README167 lines
## https://sploitus.com/exploit?id=091C0973-0963-54FE-B3B3-D2FE0198B351
# CVE-2026-52617 β€” @sworddut/mcp-ffmpeg-helper: OS command injection via ffmpeg tool options

`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](#mitigation) for what users
> can do in the meantime.

| | |
|---|---|
| CVE | CVE-2026-52617 |
| CWE | CWE-78 (Improper Neutralization of Special Elements used in an OS Command) |
| Package | [`@sworddut/mcp-ffmpeg-helper`](https://www.npmjs.com/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](https://github.com/s1ko), `s1ko@riseup.net`) |
| CVE assigned | 2026-07-13, MITRE CNA-LR |

## Threat model

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.

## Execution

`src/utils/ffmpeg.ts` β€” the command is assembled as a string and the shell is
enabled explicitly:

```ts
function runProcess(command: string, args: string[], useShell = false) {
  const child = spawn(command, args, { shell: useShell, windowsHide: true });
  …
}

export async function runFFmpegCommand(command: string): Promise {
  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` | `options` |
| `extract_audio` | `format` |
| `create_video_from_images` | `codec`, `pixelFormat`, `extraOptions` |

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"
```

### Reproduction

[`poc/driver.mjs`](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/mcp-ffmpeg-helper@0.2.1
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.

## Detection

- An `ffmpeg` command line containing `;`, `&&`, `||`, `|`, a backtick or `$(`.
  Legitimate ffmpeg invocations from this server do not.
- A `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.
- Unexpected children of the MCP server process. `ffmpeg` and `ffprobe` are
  expected; a shell, an interpreter or a network client is not.
- The server logs every invocation to stderr as
  `Running FFmpeg command: ffmpeg `, which is the cheapest place to
  spot an injected value if the logs are collected at all.
- MCP tool-call logs where `options`, `format`, `codec`, `pixelFormat` or
  `extraOptions` contain shell metacharacters.

MITRE ATT&CK [T1059.004 Command and Scripting Interpreter: Unix
Shell](https://attack.mitre.org/techniques/T1059/004/).

## Mitigation

No patched release exists, so the options are containment and removal:

- **Stop using the package** if the agent it serves processes any untrusted
  content. This is the only complete mitigation available today.
- If it must stay, run it in a container or VM as an unprivileged user, with a
  read-only filesystem apart from the media working directory, no credentials
  in its environment, and no network egress. Code execution then buys the
  attacker a sandbox rather than the host.
- Do not expose it over a network transport. The stdio-only deployment is what
  keeps this at 7.8 rather than 9.8.
- Wrap or fork the server to reject shell metacharacters in the five
  parameters listed above before they reach `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.

## Timeline

| 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 |

## References

- npm package β€” https://www.npmjs.com/package/@sworddut/mcp-ffmpeg-helper
- Repository β€” https://github.com/sworddut/mcp-ffmpeg-helper
- Precedent for the MCP threat model β€” CVE-2026-5058 (`aws-mcp-server`)

Companion advisories from the same research pass:
[CVE-2026-52616](https://github.com/s1ko/CVE-2026-52616),
[CVE-2026-52618](https://github.com/s1ko/CVE-2026-52618).

## License

MIT β€” see [LICENSE](LICENSE).