# Pickle Deserialization via __reduce__

Language: Python
Severity: Critical
CWE: CWE-502

## Source
10

## Flow
10-11-12

## Sink
12

## Vulnerable Code
```python
import pickle
import base64
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/restore', methods=['POST'])
def restore_device_config():
    encoded_state = request.json.get('device_state')
    if encoded_state:
        serialized_data = base64.b64decode(encoded_state)
        device_config = pickle.loads(serialized_data)
        return jsonify({'status': 'restored', 'config': str(device_config)})
    return jsonify({'error': 'no state provided'}), 400
```

## Explanation

The application accepts a base64-encoded serialized object from user input via request.json.get('device_state'), decodes it, and directly deserializes it using pickle.loads() without validation. Pickle deserialization of untrusted data allows arbitrary code execution through specially crafted __reduce__ methods in malicious objects.

## Remediation

The fix replaces pickle deserialization with JSON deserialization, which does not allow arbitrary code execution since JSON only supports basic data types (strings, numbers, booleans, arrays, objects, null). Additionally, a JSON schema validation step is added to ensure the deserialized configuration conforms to an expected structure, providing defense-in-depth against malformed input.

## Secure Code
```python
import json
import base64
from flask import Flask, request, jsonify
from jsonschema import validate, ValidationError

app = Flask(__name__)

# Define expected schema for device configuration
DEVICE_CONFIG_SCHEMA = {
    "type": "object",
    "properties": {
        "device_id": {"type": "string"},
        "firmware_version": {"type": "string"},
        "settings": {"type": "object"},
        "network": {"type": "object"},
        "sensors": {"type": "array"}
    },
    "required": ["device_id"],
    "additionalProperties": false
}

@app.route('/iot/device/restore', methods=['POST'])
def restore_device_config():
    encoded_state = request.json.get('device_state')
    if encoded_state:
        try:
            serialized_data = base64.b64decode(encoded_state)
            device_config = json.loads(serialized_data)
            validate(instance=device_config, schema=DEVICE_CONFIG_SCHEMA)
            return jsonify({'status': 'restored', 'config': device_config})
        except (ValueError, json.JSONDecodeError) as e:
            return jsonify({'error': 'invalid state data format'}), 400
        except ValidationError as e:
            return jsonify({'error': 'invalid device configuration schema'}), 400
    return jsonify({'error': 'no state provided'}), 400
```
