{"title":"Import Hijacking via sys.path Manipulation","language":"Python","severity":"Critical","cwe":"CWE-426","source_lines":[4,13],"flow_lines":[4,6,9,13,14,15],"sink_lines":[6,9,14,15],"vulnerable_code":"import sys\nimport os\n\ndef load_cloud_credentials(provider_dir):\n    config_path = os.path.join('/opt/cloud', provider_dir)\n    if os.path.exists(config_path):\n        sys.path.insert(0, config_path)\n    import aws_auth\n    return aws_auth.get_session_token()\n\ndef authenticate_s3_bucket(bucket_name, user_path):\n    sys.path.insert(0, user_path)\n    from cloud_utils import s3_client\n    return s3_client.connect(bucket_name)","explanation":"The code allows attackers to inject malicious Python modules by manipulating sys.path with user-controlled directory paths (provider_dir and user_path). When import statements execute after sys.path.insert(), Python searches the attacker-controlled directories first, enabling module hijacking where malicious aws_auth.py or cloud_utils.py files can be loaded instead of legitimate modules.","remediation":"The fix eliminates sys.path manipulation entirely and instead uses importlib.util.spec_from_file_location to load modules from specific, validated file paths. Provider directories are validated against an allowlist of known providers, and all resolved paths are checked to ensure they don't escape trusted base directories via symlinks or path traversal. The user_path parameter is removed from authenticate_s3_bucket since users should never control module loading locations.","secure_code":"import sys\nimport os\nimport importlib\nimport importlib.util\n\nALLOWED_PROVIDER_DIRS = frozenset(['aws', 'gcp', 'azure'])\nTRUSTED_MODULE_BASE = '/opt/cloud/modules'\n\ndef _load_module_from_trusted_path(module_name, trusted_path):\n    \"\"\"Safely load a module from a specific trusted file path without modifying sys.path.\"\"\"\n    module_file = os.path.join(trusted_path, module_name + '.py')\n    # Resolve to real path and ensure it stays within the trusted directory\n    real_module_file = os.path.realpath(module_file)\n    real_trusted_path = os.path.realpath(trusted_path)\n    if not real_module_file.startswith(real_trusted_path + os.sep):\n        raise ValueError(f\"Module path escapes trusted directory: {module_file}\")\n    if not os.path.isfile(real_module_file):\n        raise ImportError(f\"Module {module_name} not found in {trusted_path}\")\n    spec = importlib.util.spec_from_file_location(module_name, real_module_file)\n    if spec is None:\n        raise ImportError(f\"Cannot create module spec for {module_name}\")\n    module = importlib.util.module_from_spec(spec)\n    spec.loader.exec_module(module)\n    return module\n\n\ndef load_cloud_credentials(provider_dir):\n    \"\"\"Load cloud credentials from a validated provider directory.\"\"\"\n    # Validate provider_dir against allowlist\n    if provider_dir not in ALLOWED_PROVIDER_DIRS:\n        raise ValueError(f\"Invalid provider directory: {provider_dir}. Must be one of: {ALLOWED_PROVIDER_DIRS}\")\n    config_path = os.path.join(TRUSTED_MODULE_BASE, provider_dir)\n    # Ensure resolved path is still under the trusted base\n    real_config_path = os.path.realpath(config_path)\n    real_base = os.path.realpath(TRUSTED_MODULE_BASE)\n    if not real_config_path.startswith(real_base + os.sep):\n        raise ValueError(f\"Provider path escapes trusted base directory\")\n    if not os.path.isdir(real_config_path):\n        raise FileNotFoundError(f\"Provider directory does not exist: {config_path}\")\n    aws_auth = _load_module_from_trusted_path('aws_auth', real_config_path)\n    return aws_auth.get_session_token()\n\n\ndef authenticate_s3_bucket(bucket_name):\n    \"\"\"Authenticate to S3 bucket using only trusted, pre-installed modules.\"\"\"\n    # Load cloud_utils only from the trusted module base, never from user-controlled paths\n    cloud_utils = _load_module_from_trusted_path('cloud_utils', TRUSTED_MODULE_BASE)\n    return cloud_utils.s3_client.connect(bucket_name)"}