#884·pydub

ffmpeg/ffprobe argument injection via untrusted input filename in mediainfo()/mediainfo_json() (CWE-88)

Author: router0mailCreated Sep 18, 2026Updated Sep 18, 2026

Summary

pydub shells out to ffmpeg/ffprobe with an argument list (no shell, so classic shell injection is not possible). But in the media-probe helpers -- pydub.utils.mediainfo() and pydub.utils.mediainfo_json() -- the caller-supplied filepath is appended as a bare argument to the ffprobe/ffmpeg command line, with no guard against it starting with -. An attacker who can influence the input filename (an upload filename, a path built from a query parameter) can inject arbitrary ffprobe/ffmpeg options into that probe invocation. AudioSegment.from_file() calls mediainfo_json() internally, so a --prefixed input filename flows into the vulnerable probe.

Vulnerable code

pydub/utils.py, mediainfo():

python
command_args = [
    "-v", "quiet",
    "-show_format",
    "-show_streams",
    filepath                     # caller-controlled, appended as a BARE argument
]
command = [prober, '-of', 'old'] + command_args
res = Popen(command, stdout=PIPE)

pydub/utils.py, mediainfo_json() (used by from_file):

python
...
command_args += [fsdecode(filepath)]   # caller-controlled, bare argument
...
command = [prober, '-of', 'json'] + command_args
res = Popen(command, stdin=stdin_parameter, stdout=PIPE, stderr=PIPE)

prober = get_prober_name() -> ffprobe (or ffmpeg fallback). Neither function checks that filepath does not begin with -, nor is it passed after an explicit input option, so a leading - is interpreted by ffprobe/ffmpeg as an option.

Proof of concept (deterministic, captured command, no ffmpeg required)

Intercepting subprocess.Popen to capture the exact command list pydub builds:

mediainfo('song.mp3')     -> ['ffprobe','-of','old','-v','quiet','-show_format','-show_streams','song.mp3']
mediainfo('-show_entries')-> ['ffprobe','-of','old','-v','quiet','-show_format','-show_streams','-show_entries']
mediainfo('-v')           -> ['ffprobe','-of','old','-v','quiet','-show_format','-show_streams','-v']

mediainfo_json('song.mp3')      -> ['ffprobe','-of','json','-v','info','-show_format','-show_streams','song.mp3']
mediainfo_json('-show_entries')-> ['ffprobe','-of','json','-v','info','-show_format','-show_streams','-show_entries']

In the 2nd/3rd lines the injected value lands where ffprobe/ffmpeg parses it as an option (e.g. -show_entries, -v), not as the input file -- changing what tokens the probe consumes and how it behaves, driven entirely by an untrusted string the caller believed only named a file.

python
# minimal trigger (needs ffmpeg/ffprobe on PATH):
from pydub.utils import mediainfo
mediainfo("-show_entries")          # '-show_entries' parsed as an ffprobe option
from pydub import AudioSegment
AudioSegment.from_file("-af", format="mp3")   # -> mediainfo_json('-af') probe injection

Impact

Argument injection (CWE-88) into the ffprobe/ffmpeg probe invocation with caller-controlled content:

  • ffprobe: force a different output section/format (-show_entries, -of, -v), or make it consume/expect the following token as an option value.
  • ffmpeg-fallback prober: inject an option + value pair (e.g. extra -i/-af), changing what the process reads/processes.

Reachable from attacker-influenceable input filenames in applications passing user-supplied names into pydub.utils.mediainfo/mediainfo_json/AudioSegment.from_file. Not shell injection (argument list, no shell=True), so this is argument/option injection rather than arbitrary command execution -- Low-Medium severity.

Suggested fix

  • Reject/validate a leading - on the input path before it reaches the subprocess argv, or pass it after an explicit input option, or (where the tool supports it) terminate options with -- before the path.
  • Centralize the "build the ffprobe/ffmpeg argv" logic and assert the input-file token is positioned after -i / a fixed input slot.
  • Note: export(..., parameters=...) and from_file(..., parameters=...) splice a caller-provided list directly into argv (documented as "additional ffmpeg parameters") -- that's a by-design, higher-trust surface, distinct from this untrusted-filename path.

Disclosure note

github.com/jiaaro/pydub/security/advisories says "There aren't any published security advisories" and there's no SECURITY.md, so filing as a public issue.


This report was produced with AI assistance (Claude, Anthropic). The PoC intercepts subprocess.Popen to capture the exact argv pydub builds (deterministic, no ffmpeg required); the minimal trigger snippet requires ffmpeg/ffprobe on PATH and was not separately re-run in this pass -- the captured-argv evidence above is what was actually executed and verified.