T09 · Insecure Skill Coding Practices
- Location
scripts/speak.py:127- Finding
Shell Command Injection Through the Output Path
- Content
View full analysis
Vulnerability Details
File Location:
scripts/speak.py:127
Vulnerability Type: OS command injection
Risk Level: HighVulnerable code:
python if args.play and out: os.system(f"afplay {out}")Technical Analysis
The
--outputcommand-line argument is accepted without validation and passed throughspeak()asoutput. If audio generation succeeds,speak()returns this same path asout. When--playis enabled, the application interpolatesoutdirectly into a command executed byos.system().os.system()invokes a command shell. Consequently, shell metacharacters in the output path—such as command separators, substitutions, or redirections—are interpreted as shell syntax rather than as part of a filename. An attacker who can influence the program arguments can therefore append arbitrary commands.Attack Path
- The attacker causes the Skill to be invoked with
--playand a malicious--outputvalue containing shell syntax. - The supplied value reaches
speak(..., output=args.output). - If synthesis produces audio,
sf.write()writes the WAV andspeak()returns the attacker-controlled path. - Line 127 constructs
afplay {out}and passes it to the shell throughos.system(). - The shell parses the metacharacters and executes the appended command.
For example, an output argument conceptually shaped like
audio.wav; id > /tmp/proof #would result in the appendedidcommand being interpreted by the shell after successful synthesis.Impact Assessment
Successful exploitation provides arbitrary command execution with the privileges of the user running the Skill. The attacker could read or modify files accessible to that user, execute installed programs, access user-level credentials or data, make network connections, or establish user-level persistence. The vulnerability does not itself elevate privileges beyond those of the invoking process, but its sco ...[truncated 52 chars]
- The attacker causes the Skill to be invoked with
- Remediation
View remediation
Remediation Suggestions
Replace shell-based execution with an argument-vector subprocess call:
python import subprocess if args.play and out: subprocess.run(["afplay", out], check=True)Passing arguments as a list prevents the output path from being parsed as shell syntax. Do not use
shell=True.Additional hardening should include:
- Resolve the destination with
Pathand enforce any intended output-directory restrictions. - Reject unsupported output extensions or invalid path forms where appropriate.
- Handle
subprocess.CalledProcessErrorand missing-player errors without falling back to shell execution. - Add regression tests using paths containing spaces, semicolons, command substitutions, quotes, and leading hyphens to verify they are treated only as filenames.
- Resolve the destination with
