T07 · Tool Hijacking and Spoofing
- Location
scripts/run_grap_skill.py:13- Finding
Broad Exception Suppression Enables PATH-Based Tool Hijacking
- Content
View full analysis
Vulnerability Details
File Location:
scripts/run_grap_skill.py, lines 13-38
Vulnerability Type: Untrusted executable resolution and fail-open exception handling
Risk Level: MediumVulnerable Code
python try: from auto_grap_skill.cli import main as local_main old_argv = sys.argv[:] try: sys.argv = ["grap-skill", *sys.argv[1:]] return int(local_main() or 0) finally: sys.argv = old_argv except Exception: pass module_command = [sys.executable, "-m", "auto_grap_skill", *sys.argv[1:]] module_probe = subprocess.run( [sys.executable, "-c", "import auto_grap_skill"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) if module_probe.returncode == 0: return subprocess.call(module_command) command = shutil.which("grap-skill") if command: return subprocess.call([command, *sys.argv[1:]])Technical Analysis
The wrapper catches every
Exceptionraised not only while importing the bundled package, but also while executinglocal_main(). It silently discards the original failure and continues through alternative execution mechanisms.If the module probe fails, the wrapper resolves
grap-skillfrom the process'sPATHusingshutil.which()and executes the resulting file without validating its origin, ownership, permissions, or integrity. Consequently, a malicious executable namedgrap-skillin a higher-priority PATH directory can be mistaken for the legitimate tool.This is especially problematic because the subprocess used for the module probe does not automatically inherit the in-process
sys.path.insert()modification. Therefore, after a runtime exception from the bundled implementation, the probe can fail when the package is not separately installed, even though bundled source was initially found and imported successfully.No shell interpolation is used, so this is not shell command injection. Th ...[truncated 1723 chars]
- Remediation
View remediation
Remediation Suggestions
-
Catch only import-specific failures around the import statement:
python try: from auto_grap_skill.cli import main as local_main except ImportError as exc: local_main = None import_error = excOnce
local_main()starts, allow its runtime errors to propagate or report them and terminate with a nonzero status. Do not reinterpret command failures as evidence that another executable should be selected. -
Remove the PATH-based fallback entirely from the bundled skill. Prefer the bundled package, whose location is derived from
__file__, and fail closed if it cannot be loaded. -
If an external fallback is strictly required, require an explicit, trusted absolute executable path supplied through secure configuration. Validate that the executable:
- Is an absolute path.
- Is a regular file and not an unexpected symbolic link.
- Is not located in a broadly writable directory.
- Has expected ownership and permissions.
- Optionally matches a pinned cryptographic digest.
-
Run the module fallback with an explicit trusted import path rather than relying on ambient interpreter state. For example, construct a controlled environment containing the bundled source in
PYTHONPATH, or execute the known bundled entrypoint directly. -
Log the original exception and selected execution path. Silent exception suppression obscures both operational failures and exploitation attempts.
-
Add security tests that place a fake
grap-skillexecutable at the front ofPATH, forcelocal_main()to fail, and verify that the fake executable is never invoked.
-
