{"title":"Pickle Deserialization via pickle.loads() on Untrusted Network Data","language":"Python","severity":"Critical","cwe":"CWE-502","source_lines":[5,6],"flow_lines":[5,6,7],"sink_lines":[7],"vulnerable_code":"import pickle\nimport socket\n\ndef receive_iot_telemetry(device_socket):\n    telemetry_size = int(device_socket.recv(8).decode())\n    serialized_metrics = device_socket.recv(telemetry_size)\n    sensor_data = pickle.loads(serialized_metrics)\n    temperature = sensor_data.get('temp', 0)\n    humidity = sensor_data.get('humidity', 0)\n    device_id = sensor_data.get('device_id', 'unknown')\n    print(f\"Device {device_id}: Temp={temperature}C, Humidity={humidity}%\")\n    return sensor_data","explanation":"The code receives untrusted serialized data from a network socket and directly deserializes it using pickle.loads() without validation. An attacker controlling the IoT device or intercepting network traffic can send a malicious pickle payload that executes arbitrary Python code during deserialization, leading to remote code execution on the gateway.","remediation":"The fix replaces pickle.loads() with json.loads() to use a safe deserialization format that cannot execute arbitrary code. Additionally, it adds input validation including payload size limits, allowed key whitelisting, and value type checking to ensure only expected telemetry data is processed.","secure_code":"import json\nimport socket\n\nALLOWED_KEYS = {'temp', 'humidity', 'device_id', 'pressure', 'battery', 'timestamp'}\nMAX_PAYLOAD_SIZE = 4096\n\ndef receive_iot_telemetry(device_socket):\n    size_header = device_socket.recv(8).decode().strip()\n    if not size_header.isdigit():\n        raise ValueError(\"Invalid size header received\")\n    telemetry_size = int(size_header)\n    if telemetry_size > MAX_PAYLOAD_SIZE:\n        raise ValueError(f\"Payload size {telemetry_size} exceeds maximum allowed {MAX_PAYLOAD_SIZE}\")\n    serialized_metrics = device_socket.recv(telemetry_size)\n    try:\n        sensor_data = json.loads(serialized_metrics)\n    except json.JSONDecodeError as e:\n        raise ValueError(f\"Invalid JSON telemetry data: {e}\")\n    if not isinstance(sensor_data, dict):\n        raise ValueError(\"Telemetry data must be a JSON object\")\n    validated_data = {}\n    for key, value in sensor_data.items():\n        if key not in ALLOWED_KEYS:\n            continue\n        if not isinstance(value, (str, int, float, bool, type(None))):\n            raise ValueError(f\"Invalid value type for key '{key}'\")\n        validated_data[key] = value\n    temperature = validated_data.get('temp', 0)\n    humidity = validated_data.get('humidity', 0)\n    device_id = str(validated_data.get('device_id', 'unknown'))\n    print(f\"Device {device_id}: Temp={temperature}C, Humidity={humidity}%\")\n    return validated_data"}