# XML External Entity (XXE) via lxml.etree.parse on Untrusted XML

Language: Python
Severity: Critical
CWE: CWE-611

## Source
6

## Flow
6-7

## Sink
7

## Vulnerable Code
```python
from lxml import etree
import boto3

def process_iot_device_config(device_xml_payload):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(resolve_entities=True)
    config_tree = etree.parse(device_xml_payload, parser)
    device_id = config_tree.find('.//DeviceID').text
    firmware_url = config_tree.find('.//FirmwareUpdateURL').text
    telemetry_interval = config_tree.find('.//TelemetryInterval').text
    config_data = {'device': device_id, 'firmware': firmware_url, 'interval': telemetry_interval}
    s3_client.put_object(Bucket='iot-device-configs', Key=f'{device_id}.json', Body=str(config_data))
    return {'status': 'configured', 'device_id': device_id, 'next_update': firmware_url}
```

## Explanation

The function accepts untrusted XML input from IoT devices and parses it with resolve_entities=True enabled, allowing XML External Entity (XXE) attacks. An attacker can craft malicious XML with external entity declarations to read local files (e.g., AWS credentials from /root/.aws/credentials) or perform SSRF attacks against internal AWS metadata endpoints.

## Remediation

The fix disables XML external entity processing by setting resolve_entities=False, no_network=True, dtd_validation=False, and load_dtd=False in the XMLParser configuration. This prevents attackers from injecting malicious entity declarations that could read local files or perform SSRF attacks against internal services like the AWS metadata endpoint.

## Secure Code
```python
from lxml import etree
import boto3

def process_iot_device_config(device_xml_payload):
    s3_client = boto3.client('s3')
    parser = etree.XMLParser(
        resolve_entities=False,
        no_network=True,
        dtd_validation=False,
        load_dtd=False
    )
    config_tree = etree.parse(device_xml_payload, parser)
    device_id = config_tree.find('.//DeviceID').text
    firmware_url = config_tree.find('.//FirmwareUpdateURL').text
    telemetry_interval = config_tree.find('.//TelemetryInterval').text
    config_data = {'device': device_id, 'firmware': firmware_url, 'interval': telemetry_interval}
    s3_client.put_object(Bucket='iot-device-configs', Key=f'{device_id}.json', Body=str(config_data))
    return {'status': 'configured', 'device_id': device_id, 'next_update': firmware_url}
```
