T07 · Tool Hijacking and Spoofing
Error
- Location
- scripts/voice2need/adapters/discord.py:190
- Finding
- Configurable Discord Export Executable Receives the Discord Token and Full Parent Environment<![CDATA[ ## Vulnerability Details **File Location**: `scripts/voice2need/adapters/discord.py:190-201` and `scripts/voice2need/adapters/discord.py:245-250` **Vulnerability Type**: Untrusted local tool selection and excessive environment inheritance **Risk Level**: High ### Vulnerable Code ```python def _export_one(ctx, job, command, token, channel, relative, timeout): """One process and one DCE worker; private output never reaches the console.""" directory = ctx.path(relative) directory.mkdir(parents=True, exist_ok=True) args = command + ["export", "-c", channel, "-f", "Json", "-o", str(directory) + os.sep, "--after", iso_time(job["start"]), "--before", iso_time(job["end"]), "--include-threads", "All", "--parallel", "1", "--media", "false", "--markdown", "false", "--respect-rate-limits", "true"] stdout, stderr, code, status = b"", b"", None, "complete" try: result = subprocess.run(args, env={**os.environ, "DISCORD_TOKEN": token}, capture_output=True, timeout=timeout, check=False) ``` The command is selected from the job configuration using only structural validation: ```python command = job.get("dce_command") if not isinstance(command, list) or not command or not all(isinstance(c, str) for c in command): raise SourceError("invalid-input", "Set dce_command to an installed executable, or dotnet plus its DLL") if not (len(command) == 1 or (len(command) == 2 and Path(command[0]).name == "dotnet" and command[1].lower().endswith(".dll"))): raise SourceError("invalid-input", "dce_command accepts only one executable or dotnet plus its DLL; no flags") ``` ### Technical Analysis The validation limits the number and shape of command elements, which prevents direct shell-argument injection because `subprocess.run` is invoked without a shell. It does not, however, verify that the selected executable is ...[truncated 2413 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Allowlist the expected exporter** - Require an absolute path. - Resolve symlinks before validation. - Verify that the resolved executable is an approved DiscordChatExporter binary. - For DLL execution, allow only an explicitly approved absolute DLL path. 2. **Verify tool integrity** - Pin an expected version. - Validate a cryptographic hash or trusted code signature before execution. - Record the resolved path, version, and hash in the run manifest. - Require renewed authorization if any of those values change. 3. **Use a minimal child environment** - Do not copy all of `os.environ`. - Pass only `DISCORD_TOKEN` and narrowly required runtime variables. - If `PATH` is needed, construct a controlled value rather than inheriting an untrusted one. - Explicitly exclude cloud, CI, package-registry, SSH-agent, and unrelated API credentials. 4. **Strengthen authorization** - Treat a change in `dce_command` as a material scope change. - Require explicit approval of the resolved executable path in addition to Discord account and channel confirmation. - Persist the approved tool identity in the run checkpoint and reject mismatches on resume. 5. **Reduce process privileges** - Run the exporter in a restricted subprocess environment or sandbox. - Limit filesystem access to its output directory. - Restrict outbound network access to Discord endpoints where practical. ]]>
