Canonical LXD CVE-2026-34178: Brief Summary of a Critical Project Restriction Bypass via Backup Import

A brief summary of CVE-2026-34178, a critical CVSS 9.1 vulnerability in Canonical LXD where a trust boundary mismatch in the backup import path allows authenticated users to bypass project restrictions and achieve full host compromise. Includes patch analysis and affected version details.

CVE Analysis

9 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-09

Canonical LXD CVE-2026-34178: Brief Summary of a Critical Project Restriction Bypass via Backup Import
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A validation discrepancy in Canonical LXD's backup import path allowed authenticated users to smuggle privileged container configurations past project restriction enforcement, achieving full host compromise from within a supposedly locked down project. For any organization running multi tenant LXD environments, this vulnerability (scored CVSS 9.1) effectively dissolved the security boundary that project restrictions were designed to enforce.

LXD is a system container and virtual machine manager written in Go, built on top of LXC, and maintained by Canonical. It is widely used in cloud infrastructure, development environments, and multi tenant hosting scenarios where project level isolation is a core security control. Canonical took full ownership of the LXD project in July 2023, and it ships as a snap package across multiple supported release tracks.

Technical Information

Root Cause: Dual Configuration Sources with No Integrity Linkage

LXD projects support a "restricted" mode designed to enforce security boundaries. In restricted projects, operations like creating privileged containers or passing through host devices are blocked regardless of the user's privilege level within that project. CVE-2026-34178 completely bypasses these protections due to a configuration synchronization failure during the backup import path.

A single LXD backup tar archive contains two distinct configuration sources:

  • backup/index.yaml: A metadata file read by backup.GetInfo() during import validation.
  • backup/container/backup.yaml: The full instance configuration that gets extracted to the storage volume and is used to actually build the instance.

The AllowInstanceCreation() function validates project restrictions using only the configuration parsed from index.yaml. However, the internalImportFromBackup function reads backup.yaml from the storage mount path to construct the instance database record and create the instance. Critically, this creation function only validates the format of configuration keys, not their compliance with project restrictions.

Because both files live inside the same attacker controlled tar archive with no integrity linkage between them, an attacker can construct an archive where index.yaml carries clean, restriction compliant configuration while backup.yaml smuggles in dangerous settings.

Attack Flow

Exploitation requires the attacker to hold can_view_instances, can_create_instances, and can_operate_instances permissions on the target project. These are standard permissions for any user expected to manage instances within a restricted project. The attack proceeds as follows:

  1. The attacker locally constructs a backup directory structure containing a minimal root filesystem.
  2. The attacker creates a benign backup/index.yaml that contains no restricted settings and will pass all project restriction checks.
  3. The attacker creates a malicious backup/container/backup.yaml containing security.privileged: "true" and raw.lxc directives that bind mount the host LXD unix socket (/var/snap/lxd/common/lxd/unix.socket) into the container.
  4. The attacker packages these files into a tar archive and imports it to the target LXD server, specifying the restricted project.
  5. The AllowInstanceCreation() check passes because it only inspects index.yaml.
  6. The instance is created from the unchecked backup.yaml, resulting in a privileged container with a host socket mount.
  7. The attacker starts the container and accesses the bind mounted LXD Unix socket. Local connections over this socket are trusted as full admin, granting the attacker unrestricted access across all projects on the host and full cluster admin privileges.

CVSS v3.1 Breakdown

The 9.1 Critical score reflects the following base metrics:

MetricValueOperational Implication
Attack VectorNetworkExploit delivered remotely via the LXD API during backup import
Attack ComplexityLowNo advanced timing or race conditions required
Privileges RequiredHighAttacker must have instance creation rights in a project
User InteractionNoneNo action required from the server administrator
ScopeChangedEscape from the restricted project to the host system
ConfidentialityHighFull host compromise exposes all data on the cluster
IntegrityHighFull administrative control to modify any instance
AvailabilityHighAbility to disrupt or destroy any service on the host

The combination of a network attack vector with changed scope makes this particularly severe for shared hosting and multi tenant deployments.

Patch Information

The fix for CVE-2026-34178 was merged via Pull Request #17921 ("Import: Create backup config from index") into the canonical/lxd repository on March 24, 2026, with merge commit 06637bfd. The patch was authored by contributor roosterfish and merged by tomponline (Tom Parrott, Canonical).

The patch eliminates the dual source inconsistency through a single source of truth strategy, making index.yaml the only configuration source, across 8 changed files with 199 additions and 116 deletions. Here is how each piece fits together:

The Critical Pivot: lxd/api_internal.go

The internalImportFromBackup function previously read instance config from the on disk backup.yaml file extracted to the storage volume. The patch changes its signature to accept a *backup.Info struct (populated from index.yaml) and directly uses it:

// Before: read untrusted backup.yaml from storage mount backupYamlPath := filepath.Join(instanceMountPoint, "backup.yaml") backupConf, err := backup.ParseConfigYamlFile(backupYamlPath) // After: use the already-validated index config backupConf := bInfo.Config

By never reading the separate backup.yaml for instance creation, the attacker controlled secondary file is completely cut out of the trust chain.

Eliminating the Disk Round Trip: lxd/backup/backup_config_utils.go

The old UpdateInstanceConfig function read backup.yaml from disk, selectively merged a few fields (Name, Project, pool info), then wrote it back. Critically, it did not overwrite Instance.Config or Instance.Devices, which are exactly the fields that carried the attacker's payload. The replacement function UpdateInstanceConfigInPlace operates entirely on the in memory Info struct and never touches backup.yaml on disk. Roughly 50 lines of file I/O and partial merge logic were removed.

Wiring Up the New Flow: lxd/instances_post.go

The createFromBackup function now calls UpdateInstanceConfigInPlace on the bInfo object before handing it to internalImportFromBackup. The full bInfo struct is passed instead of individual projectName and instName strings:

err = backup.UpdateInstanceConfigInPlace(s.DB.Cluster, bInfo) if err != nil { return response.SmartError(fmt.Errorf("Failed updating backup index file in place: %w", err)) } // ... err = internalImportFromBackup(ctx, s, bInfo, instanceName != "", devices)

Defense in Depth: Excluding backup.yaml from Exports

In lxd/storage/drivers/generic_vfs.go, new exports now explicitly skip backup.yaml from the tar archive. This means that going forward, backup tarballs will only contain index.yaml, eliminating the secondary config file as an attack surface entirely:

alwaysExcludedPaths := []string{ filepath.Join(mountPath, "backup.yaml"), }

Additional Changes

The backupWriteIndex function in lxd/backup.go was updated to include custom storage volume configuration in the index (previously omitted), so that index.yaml now carries enough information to fully reconstruct instance config without needing backup.yaml. The old update path in lxd/storage/backend_lxd.go that mounted the volume and called UpdateInstanceConfig on the on disk backup.yaml was deleted entirely.

A new integration test test_backup_inconsistent_config explicitly verifies that importing an archive where the index contains security.privileged: "true" in a restricted project is rejected, that a clean index succeeds, and that exported backups no longer contain backup/container/backup.yaml.

Patched Versions

Interim snap releases delivering the fix were published on March 30, 2026:

LXD SeriesPatched VersionInterim Snap Release
6.x6.86.7-d814d89
5.21.x LTS5.21.55.21.4-aee7e08
5.0.x LTS5.0.75.0.6-7fc3b36
4.0.x LTSN/A4.0.10-e92d947

Administrators should immediately refresh their snap installations to the interim releases or upgrade to the final patched versions.

Affected Systems and Versions

According to the GitHub Security Advisory, CVE-2026-34178 affects all LXD versions from 4.12 onward. Specifically:

  • LXD 6.x series: all versions prior to 6.8
  • LXD 5.21.x LTS series: all versions prior to 5.21.5
  • LXD 5.0.x LTS series: all versions prior to 5.0.7
  • LXD 4.x series: versions from 4.12 onward

The vulnerability is only exploitable in environments where LXD projects are configured with restricted mode enabled and where users have been granted instance creation permissions within those restricted projects. Single tenant deployments where all users already have full admin access are not meaningfully impacted, as those users already possess the privileges this vulnerability grants.

Vendor Security History

Canonical maintains a robust security tracking infrastructure, tracking all Common Vulnerabilities and Exposures affecting Ubuntu and its official packages. When security issues are resolved, Canonical developers issue Ubuntu Security Notices to inform the public. The prompt response to CVE-2026-34178, including the simultaneous release of interim snap builds across four different release tracks on March 30, 2026, demonstrates a mature security incident response capability. The detailed security advisory published on GitHub, including full CVSS scoring and remediation guidance, reflects Canonical's transparent approach to vulnerability disclosure.

References

Detect & fix
what others miss

Security magnifying glass visualization