{"title":"Zip Slip via zipfile.extractall() on Untrusted Archives","language":"Python","severity":"Critical","cwe":"CWE-22","source_lines":[8],"flow_lines":[8,9,10,11],"sink_lines":[11],"vulnerable_code":"import zipfile\nimport os\nfrom flask import request, jsonify\n\n@app.route('/api/iot/firmware-update', methods=['POST'])\ndef deploy_firmware_package():\n    device_id = request.form.get('device_id')\n    firmware_archive = request.files['firmware']\n    deployment_path = f'/opt/iot/devices/{device_id}/firmware'\n    os.makedirs(deployment_path, exist_ok=True)\n    archive_path = f'/tmp/{firmware_archive.filename}'\n    firmware_archive.save(archive_path)\n    with zipfile.ZipFile(archive_path, 'r') as fw_zip:\n        fw_zip.extractall(deployment_path)\n    return jsonify({'status': 'deployed', 'device': device_id})","explanation":"The application accepts an uploaded firmware archive from an untrusted source without validating the file paths contained within the zip. When extractall() is called on line 14, malicious zip entries with path traversal sequences (e.g., '../../etc/cron.d/backdoor') can write files outside the intended deployment directory, leading to arbitrary file write and potential remote code execution.","remediation":"The fix iterates over all entries in the zip archive before extraction and resolves each member's target path using os.path.realpath(). It then verifies that every resolved path starts with the intended deployment directory, rejecting the archive entirely if any entry attempts path traversal. Additionally, the temporary archive file is cleaned up after extraction.","secure_code":"import zipfile\nimport os\nfrom flask import request, jsonify\n\n@app.route('/api/iot/firmware-update', methods=['POST'])\ndef deploy_firmware_package():\n    device_id = request.form.get('device_id')\n    firmware_archive = request.files['firmware']\n    deployment_path = os.path.realpath(f'/opt/iot/devices/{device_id}/firmware')\n    os.makedirs(deployment_path, exist_ok=True)\n    archive_path = f'/tmp/{firmware_archive.filename}'\n    firmware_archive.save(archive_path)\n    with zipfile.ZipFile(archive_path, 'r') as fw_zip:\n        for member in fw_zip.infolist():\n            member_path = os.path.realpath(os.path.join(deployment_path, member.filename))\n            if not member_path.startswith(deployment_path + os.sep) and member_path != deployment_path:\n                return jsonify({'status': 'error', 'message': f'Malicious path detected in archive: {member.filename}'}), 400\n        fw_zip.extractall(deployment_path)\n    os.remove(archive_path)\n    return jsonify({'status': 'deployed', 'device': device_id})"}