# Import Hijacking via sys.path Precedence

Language: Python
Severity: Critical
CWE: CWE-426

## Source
4

## Flow
4-5-6

## Sink
6

## Vulnerable Code
```python
import sys
import os

def initialize_iot_device_manager(device_config_path):
    config_dir = os.path.dirname(device_config_path)
    if config_dir not in sys.path:
        sys.path.insert(0, config_dir)
    import crypto_utils
    import device_auth
    device_id = device_auth.generate_device_id()
    encryption_key = crypto_utils.derive_key(device_id)
    return {
        'device_id': device_id,
        'encryption_key': encryption_key,
        'status': 'initialized'
    }

def register_iot_device(device_name, config_file):
    device_info = initialize_iot_device_manager(config_file)
    import requests
    response = requests.post('https://iot-hub.example.com/register', json={
        'name': device_name,
        'device_id': device_info['device_id'],
        'key': device_info['encryption_key']
    })
    return response.json()
```

## Explanation

The function accepts an untrusted user-controlled path (device_config_path), extracts its directory, and inserts it at the beginning of sys.path. This allows an attacker to place malicious Python modules (crypto_utils.py or device_auth.py) in the configuration directory, which will be imported instead of legitimate modules, hijacking cryptographic operations and authentication flows.

## Remediation

The fix eliminates the sys.path manipulation entirely and instead loads security-critical modules (crypto_utils, device_auth) exclusively from a hardcoded trusted directory using importlib.util. It also validates that resolved file paths stay within the trusted directory to prevent symlink attacks, ensuring that user-controlled config paths cannot influence which modules are loaded.

## Secure Code
```python
import sys
import os
import importlib.util

# Define the trusted directory where legitimate modules must reside
TRUSTED_MODULE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'trusted_modules'))

def _load_module_from_trusted_path(module_name):
    """Load a module exclusively from the trusted module directory."""
    module_path = os.path.join(TRUSTED_MODULE_DIR, f"{module_name}.py")
    if not os.path.isfile(module_path):
        raise ImportError(
            f"Module '{module_name}' not found in trusted directory: {TRUSTED_MODULE_DIR}"
        )
    # Resolve symlinks and verify the real path is within the trusted directory
    real_path = os.path.realpath(module_path)
    if not real_path.startswith(os.path.realpath(TRUSTED_MODULE_DIR) + os.sep):
        raise ImportError(
            f"Module '{module_name}' resolves outside trusted directory (possible symlink attack)"
        )
    spec = importlib.util.spec_from_file_location(module_name, real_path)
    if spec is None:
        raise ImportError(f"Cannot create module spec for '{module_name}'")
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def initialize_iot_device_manager(device_config_path):
    """Initialize the IoT device manager using modules from a trusted directory only."""
    # Validate config file exists (but do NOT modify sys.path based on it)
    if not os.path.isfile(device_config_path):
        raise FileNotFoundError(f"Configuration file not found: {device_config_path}")

    # Load crypto and auth modules exclusively from the trusted directory
    crypto_utils = _load_module_from_trusted_path('crypto_utils')
    device_auth = _load_module_from_trusted_path('device_auth')

    device_id = device_auth.generate_device_id()
    encryption_key = crypto_utils.derive_key(device_id)
    return {
        'device_id': device_id,
        'encryption_key': encryption_key,
        'status': 'initialized'
    }


def register_iot_device(device_name, config_file):
    device_info = initialize_iot_device_manager(config_file)
    import requests
    response = requests.post('https://iot-hub.example.com/register', json={
        'name': device_name,
        'device_id': device_info['device_id'],
        'key': device_info['encryption_key']
    })
    return response.json()
```
