T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/tts_walkie.sh:27
- Finding
- Arbitrary Command Execution Through Unquoted Heredoc Expansion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tts_walkie.sh`, lines 27-39 **Vulnerability Type**: Shell command injection and generated Python source injection **Risk Level**: High ### Vulnerable Code ```bash python3 << PYEOF from kittentts import KittenTTS import numpy as np, soundfile as sf, subprocess, sys, os text = """$TEXT""" voice = "$VOICE" speed = float("$VOICE_SPEED") tts = KittenTTS("KittenML/kitten-tts-mini-0.8") audio = tts.generate(text, voice=voice, speed=speed) # speed now actually used wav_path = "$WAV_PATH" ogg_path = "$OGG_PATH" ``` ### Technical Analysis The heredoc delimiter `PYEOF` is not quoted. Bash therefore performs parameter expansion, command substitution, and related shell processing on the heredoc body before passing it to Python. The attacker-controlled or externally influenced values `TEXT`, `VOICE`, and `VOICE_SPEED` are embedded directly in this body. A value containing shell command substitution, such as `$(command)`, causes the command to run in Bash before Python starts. Quotation marks surrounding the expanded values do not prevent this processing because they are heredoc content rather than shell syntax protecting the expansion. Directly embedding these values into Python source also creates a second injection surface. Crafted quotes, backslashes, or newline sequences can terminate the intended Python string and introduce arbitrary Python statements. The skill metadata identifies the skill as privileged because of its installation requirements. Although these scripts do not elevate privileges themselves, command injection obtains all privileges of whichever account invokes the script. Running it as root would therefore turn this into root-level arbitrary command execution. ### Attack Path 1. An attacker supplies or influences the text, voice argument, or `VOICE_SPEED` environment variable passed to `tts_walkie.sh`. 2. The malicious value contains command substitution, for example a value struct ...[truncated 962 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Quote the heredoc delimiter so Bash does not expand its contents. - Pass user-controlled values as command-line arguments or environment variables rather than generating Python source from them. - Restrict `VOICE` to the documented allowlist. - Parse `VOICE_SPEED` as a numeric value and enforce safe minimum and maximum bounds. - Avoid invoking the runtime script with root privileges. A safer pattern is: ```bash python3 - "$TEXT" "$VOICE" "$VOICE_SPEED" "$WAV_PATH" "$OGG_PATH" <<'PYEOF' from kittentts import KittenTTS import soundfile as sf import subprocess import sys text, voice, speed_raw, wav_path, ogg_path = sys.argv[1:] allowed_voices = { "Bella", "Jasper", "Luna", "Bruno", "Rosie", "Hugo", "Kiki", "Leo" } if voice not in allowed_voices: raise SystemExit("Invalid voice") try: speed = float(speed_raw) except ValueError: raise SystemExit("Invalid voice speed") if not 0.5 <= speed <= 2.0: raise SystemExit("Voice speed is outside the permitted range") tts = KittenTTS("KittenML/kitten-tts-mini-0.8") audio = tts.generate(text, voice=voice, speed=speed) sf.write(wav_path, audio, 24000) subprocess.run( [ "ffmpeg", "-y", "-i", wav_path, "-ar", "16000", "-ac", "1", "-c:a", "libopus", "-b:a", "128k", ogg_path ], check=True ) PYEOF ``` ]]>
