T07 · Tool Hijacking and Spoofing
- Location
scripts/_stack_invoke.py:13- Finding
Unverified External Tool Resolution Allows Local Tool Hijacking
- Content
View full analysis
Vulnerability Details
File Location:
scripts/_stack_invoke.py, lines 13–37
Vulnerability Type: Untrusted local tool discovery and execution
Risk Level: MediumVulnerable Code
python def stack_root() -> Path: env = os.environ.get("LYGO_STACK_ROOT", "").strip() if env: return Path(env).resolve() for p in HERE.parents: if (p / "tools" / "collect_living_mesh_badge.py").is_file(): return p if (p / "tools" / "verify_all_kernel_layers.py").is_file(): return p if (p / "docs" / "network_builder" / "IMMUTABLE_ANCHORS.json").is_file(): return p return Path.cwd() def invoke(tool_name: str, argv: list[str] | None = None) -> int: stack = stack_root() tool = stack / "tools" / tool_name if not tool.is_file(): print( f'{{"verdict":"ERROR","reason":"missing_tool","tool":"{tool_name}","stack":"{stack}","hint":"set LYGO_STACK_ROOT"}}', file=sys.stderr, ) return 2 cmd = [sys.executable, str(tool), *(argv if argv is not None else sys.argv[1:])] return subprocess.call(cmd, cwd=str(stack))Technical Analysis
Every advertised wrapper delegates its operation to a Python script located outside the audited package. The stack directory can be selected through the
LYGO_STACK_ROOTenvironment variable, inferred from ancestor directories using marker files, or taken directly from the current working directory.Before execution, the implementation verifies only that the selected path is a regular file. It does not establish that the stack directory is trusted or that the target script is an authentic LYGO component. There is no validation of file ownership, permissions, expected version, cryptographic digest, digital signature, or approved installation root.
Consequently, a malicious or compromised local directory can provide files with ...[truncated 2712 chars]
- Remediation
View remediation
Remediation Suggestions
-
Use an explicitly configured trusted installation root
- Remove the
Path.cwd()fallback. - Avoid heuristic ancestor discovery based solely on marker-file existence.
- Resolve tools only from an installation directory established and protected by the package manager or administrator.
- Remove the
-
Validate paths before execution
- Canonicalize the root and target with
Path.resolve(strict=True). - Confirm that the tool remains beneath the approved root after resolution.
- Reject symbolic links or other path redirections when they are not explicitly required.
- On supported systems, verify that the directory and tool are not writable by untrusted users.
- Canonicalize the root and target with
-
Verify tool authenticity
- Maintain an allowlist of expected tool names and cryptographic SHA-256 digests.
- Verify a signed manifest before invoking any external tool.
- Reject unknown versions or hash mismatches rather than continuing with a warning.
-
Bundle security-critical implementations
- Prefer shipping the actual implementations within the audited Skill package.
- If external integration is necessary, expose a versioned API with explicit compatibility and integrity checks instead of executing arbitrary filesystem scripts.
-
Harden environment-based configuration
- Do not trust
LYGO_STACK_ROOTin privileged, automated, or untrusted execution contexts. - Require an explicit command-line configuration or administrator-controlled configuration file.
- Emit the resolved canonical executable path and verified version before execution.
- Do not trust
-
Apply least privilege
- Run delegated tools without unnecessary credentials or sensitive environment variables.
- Restrict filesystem and network access through an appropriate sandbox where feasible.
-
