# Import Hijacking via sys.path Manipulation

Language: Python
Severity: Critical
CWE: CWE-427

## Source
4

## Flow
4-8

## Sink
8

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

def load_cloud_credentials(provider_path):
    credential_dir = os.path.join('/tmp/cloud_configs', provider_path)
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    sys.path.insert(0, credential_dir)
    import aws_auth
    session = aws_auth.create_session()
    return session.get_credentials()

def authenticate_s3_bucket(bucket_name, config_path):
    creds = load_cloud_credentials(config_path)
    return creds
```

## Explanation

The function accepts user-controlled `provider_path` and directly constructs a directory path that is inserted at the beginning of sys.path without validation. An attacker can use path traversal (e.g., '../../../attacker_dir') to inject a malicious directory containing a crafted aws_auth.py module, which will be imported and executed with the application's privileges.

## Remediation

The fix eliminates sys.path manipulation entirely and instead uses importlib.util to load the module directly from a validated file path. It sanitizes the provider_path input by rejecting path traversal characters and non-alphanumeric names, then verifies the resolved absolute path stays within the allowed base directory before loading any module.

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

ALLOWED_PROVIDERS = None  # Set to a list of allowed provider names to restrict further
BASE_CREDENTIAL_DIR = '/tmp/cloud_configs'

def load_cloud_credentials(provider_path):
    # Sanitize: only allow simple directory names, no path separators or traversal
    if not provider_path or '..' in provider_path or os.sep in provider_path or '/' in provider_path:
        raise ValueError(f"Invalid provider path: '{provider_path}'. Path traversal is not allowed.")
    
    # Only allow alphanumeric characters, hyphens, and underscores
    if not all(c.isalnum() or c in ('-', '_') for c in provider_path):
        raise ValueError(f"Invalid provider path: '{provider_path}'. Only alphanumeric characters, hyphens, and underscores are allowed.")
    
    # If an allowlist is configured, enforce it
    if ALLOWED_PROVIDERS is not None and provider_path not in ALLOWED_PROVIDERS:
        raise ValueError(f"Provider '{provider_path}' is not in the list of allowed providers.")
    
    credential_dir = os.path.join(BASE_CREDENTIAL_DIR, provider_path)
    
    # Resolve to absolute path and verify it's still under the base directory
    credential_dir = os.path.realpath(credential_dir)
    base_dir_resolved = os.path.realpath(BASE_CREDENTIAL_DIR)
    if not credential_dir.startswith(base_dir_resolved + os.sep):
        raise ValueError(f"Resolved path escapes the allowed base directory.")
    
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir, mode=0o750)
    
    # Load the module directly from the expected file rather than manipulating sys.path
    module_file = os.path.join(credential_dir, 'aws_auth.py')
    if not os.path.isfile(module_file):
        raise FileNotFoundError(f"AWS auth module not found at: {module_file}")
    
    # Verify the module file is within the allowed directory
    module_file_resolved = os.path.realpath(module_file)
    if not module_file_resolved.startswith(credential_dir + os.sep) and module_file_resolved != os.path.join(credential_dir, 'aws_auth.py'):
        raise ValueError("Module file path escapes the allowed credential directory.")
    
    spec = importlib.util.spec_from_file_location(f"aws_auth_{provider_path}", module_file)
    aws_auth = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(aws_auth)
    
    session = aws_auth.create_session()
    return session.get_credentials()

def authenticate_s3_bucket(bucket_name, config_path):
    creds = load_cloud_credentials(config_path)
    return creds
```
