# Billion Laughs Denial of Service via XML Entity Expansion

Language: Python
Severity: High
CWE: CWE-776

## Source
8

## Flow
8-10

## Sink
10

## Vulnerable Code
```python
import xml.etree.ElementTree as ET
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/config', methods=['POST'])
def process_device_config():
    device_xml = request.data
    try:
        config_tree = ET.fromstring(device_xml)
        device_id = config_tree.find('device_id').text
        settings = {child.tag: child.text for child in config_tree.find('settings')}
        return jsonify({'status': 'configured', 'device': device_id, 'settings': settings})
    except Exception as e:
        return jsonify({'error': str(e)}), 400
```

## Explanation

The application parses untrusted XML data from IoT devices using ET.fromstring() without disabling external entity processing or entity expansion. This allows attackers to send specially crafted XML with recursive entity definitions that exponentially expand in memory, causing a Billion Laughs (XML bomb) denial of service attack.

## Remediation

The fix replaces the standard `xml.etree.ElementTree` module with `defusedxml.ElementTree`, which is a drop-in replacement that automatically prevents XML entity expansion attacks (Billion Laughs), external entity processing (XXE), and other XML-based attacks. The defusedxml library raises an exception when it encounters potentially malicious DTD definitions or entity expansions, stopping the attack before memory exhaustion occurs.

## Secure Code
```python
import defusedxml.ElementTree as ET
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/iot/device/config', methods=['POST'])
def process_device_config():
    device_xml = request.data
    try:
        config_tree = ET.fromstring(device_xml)
        device_id = config_tree.find('device_id').text
        settings = {child.tag: child.text for child in config_tree.find('settings')}
        return jsonify({'status': 'configured', 'device': device_id, 'settings': settings})
    except Exception as e:
        return jsonify({'error': str(e)}), 400
```
