# Module Hijacking via `sitecustomize.py` on `sys.path`

Language: Python
Severity: Critical
CWE: CWE-427

## Source
3

## Flow
3-4-5

## Sink
5

## Vulnerable Code
```python
import sys
import os
cloud_config_path = os.getenv('AWS_LAMBDA_CONFIG_DIR', '/tmp/lambda_configs')
if cloud_config_path not in sys.path:
    sys.path.insert(0, cloud_config_path)
import boto3
import sitecustomize
def deploy_serverless_function(func_name, runtime_env, iam_role):
    s3_client = boto3.client('s3')
    lambda_client = boto3.client('lambda')
    deployment_pkg = sitecustomize.prepare_deployment_package(func_name, runtime_env)
    bucket_name = sitecustomize.get_deployment_bucket()
    s3_client.upload_file(deployment_pkg, bucket_name, f'{func_name}.zip')
    response = lambda_client.create_function(FunctionName=func_name, Runtime=runtime_env, Role=iam_role, Handler='index.handler', Code={'S3Bucket': bucket_name, 'S3Key': f'{func_name}.zip'})
    return response['FunctionArn']
```

## Explanation

The code reads an untrusted environment variable AWS_LAMBDA_CONFIG_DIR and inserts it at the beginning of sys.path without validation. An attacker controlling this environment variable can place a malicious sitecustomize.py in that directory, which will be automatically imported by Python and executed with full privileges, allowing credential theft, code injection into Lambda packages, or IAM role exfiltration.

## Remediation

The fix eliminates the module hijacking vulnerability by: (1) removing the use of an untrusted environment variable to modify sys.path, replacing it with a hardcoded allowed directory; (2) using importlib to explicitly load a specifically-named module from a validated path instead of importing `sitecustomize` which has special Python semantics; (3) adding file ownership and permission checks to ensure the loaded module hasn't been tampered with by an unprivileged attacker.

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

# Use a hardcoded, secure path for configuration instead of trusting environment variables
ALLOWED_CONFIG_DIR = '/opt/lambda_configs'

def _load_deployment_module(module_path):
    """Safely load the deployment helper module from a trusted, validated path."""
    expected_module = os.path.join(module_path, 'deployment_helper.py')
    
    # Validate the path is the expected hardcoded directory
    real_path = os.path.realpath(expected_module)
    if not real_path.startswith(os.path.realpath(ALLOWED_CONFIG_DIR) + os.sep):
        raise RuntimeError(f"Module path {real_path} is outside the allowed config directory")
    
    if not os.path.isfile(real_path):
        raise FileNotFoundError(f"Deployment helper module not found at {real_path}")
    
    # Validate file ownership and permissions (must be owned by root, not world-writable)
    file_stat = os.stat(real_path)
    if file_stat.st_uid != 0:
        raise PermissionError(f"Deployment helper module must be owned by root, owned by uid {file_stat.st_uid}")
    if file_stat.st_mode & 0o002:
        raise PermissionError("Deployment helper module must not be world-writable")
    
    # Load the module explicitly using importlib rather than manipulating sys.path
    spec = importlib.util.spec_from_file_location("deployment_helper", real_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module

import boto3

def deploy_serverless_function(func_name, runtime_env, iam_role):
    # Load the deployment helper module from the trusted path
    deployment_helper = _load_deployment_module(ALLOWED_CONFIG_DIR)
    
    s3_client = boto3.client('s3')
    lambda_client = boto3.client('lambda')
    
    deployment_pkg = deployment_helper.prepare_deployment_package(func_name, runtime_env)
    bucket_name = deployment_helper.get_deployment_bucket()
    
    s3_client.upload_file(deployment_pkg, bucket_name, f'{func_name}.zip')
    
    response = lambda_client.create_function(
        FunctionName=func_name,
        Runtime=runtime_env,
        Role=iam_role,
        Handler='index.handler',
        Code={'S3Bucket': bucket_name, 'S3Key': f'{func_name}.zip'}
    )
    return response['FunctionArn']
```
