CVE-2026-55622: Cross-Project Instance Copy Authorization Bypass in Canonical LXD and LXC Incus

Vulnerability ID: GHSA-QX75-2P3R-PWM5
CVSS Score: 7.7
Published: 2026-11-06

CVE-2026-55622 (GHSA-qx75-2p3r-pwm5) is an improper access control vulnerability (CWE-284) in Canonical LXD and LXC Incus (tracked as GHSA-c9f5-j9c3-mhrg). In multi-tenant deployments utilizing project-based isolation, the REST API handler for instance creation via POST /1.0/instances verifies that the authenticated user has authorization in the destination project specified by the query parameter, but fails to verify whether the user holds read or view permissions for the source instance when executing a copy operation across projects. Consequently, an authenticated client restricted to a single project can clone instances or snapshots from unauthorized projects into their own project, resulting in complete disclosure of the target instance filesystem and embedded credentials.

TL;DR

A missing authorization check in the instance creation handler (instancesPost) of Canonical LXD and LXC Incus allows an authenticated user with access to at least one project to copy instances and snapshots from any other restricted project on the same daemon or cluster. Once copied into the attacker's authorized project, the attacker gains full administrative control over the duplicate instance and its underlying data.

⚠️ Exploit Status: POC

Technical Details

  • CVE / Advisory ID: CVE-2026-55622 / GHSA-qx75-2p3r-pwm5 / GHSA-c9f5-j9c3-mhrg
  • Weakness Class: CWE-284 (Improper Access Control)
  • CVSS v3.1 Score: 7.7 High (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)
  • EPSS Score: 0.45% (39.55th Percentile)
  • Attack Vector: Network (Authenticated REST API POST /1.0/instances)
  • Primary Impact: Cross-Project Instance & Snapshot Cloning / High Confidentiality Loss
  • Exploit Status: Proof of Concept Publicly Available
  • CISA KEV Status: Not Listed

Affected Systems

  • Canonical LXD
  • LXC Incus
  • LXD (5.0 LTS): >= 4.12.0, < 5.0.7 (Fixed in: 5.0.7)
  • LXD (5.21 LTS): >= 5.1.0, < 5.21.5 (Fixed in: 5.21.5)
  • LXD (6.x series): >= 6.0.0, < 6.8.0 (Fixed in: 6.8)
  • Incus: < 7.2.0 (Fixed in: 7.2.0)

Code Analysis

Commit: 97cb037

lxd/instances_post: Check EntitlementCanView permission on source instance or snapshot during copy

--- a/lxd/instances_post.go +++ b/lxd/instances_post.go @@ -18,6 +18,7 @@ import (   "github.com/canonical/lxd/client" "github.com/canonical/lxd/lxd/archive" + "github.com/canonical/lxd/lxd/auth"  "github.com/canonical/lxd/lxd/backup" "github.com/canonical/lxd/lxd/cluster" "github.com/canonical/lxd/lxd/db" @@ -1198,6 +1199,29 @@ func instancesPost(d *Daemon, r *http.Request) response.Response { req.Config = map[string]string{} }  + if req.Source.Type == api.SourceTypeCopy { + if req.Source.Source == "" { + return response.BadRequest(errors.New("Must specify a source instance")) + } + + if req.Source.Project == "" { + req.Source.Project = targetProjectName + } + + var sourceURL *api.URL + instanceName, snapshotName, isSnapshot := api.GetParentAndSnapshotName(req.Source.Source) + if isSnapshot { + sourceURL = entity.InstanceSnapshotURL(req.Source.Project, instanceName, snapshotName) + } else { + sourceURL = entity.InstanceURL(req.Source.Project, req.Source.Source) + } + + err = s.Authorizer.CheckPermission(r.Context(), sourceURL, auth.EntitlementCanView) + if err != nil { + return response.SmartError(err) + } + } +  if req.InstanceType != "" { conf, err := instanceParseType(req.InstanceType) if err != nil { 

Commit: bc1e20e

lxd/instances_post: Verify UserHasPermission view on source project during copy in LTS branch

--- a/lxd/instances_post.go +++ b/lxd/instances_post.go @@ -923,6 +923,20 @@ func instancesPost(d *Daemon, r *http.Request) response.Response { } }  + if req.Source.Type == "copy" { + if req.Source.Source == "" { + return response.BadRequest(fmt.Errorf("Must specify a source instance")) + } + + if req.Source.Project == "" { + req.Source.Project = targetProjectName + } + + if !s.Authorizer.UserHasPermission(r, req.Source.Project, "view") { + return response.Forbidden(nil) + } + } +  var targetProject *api.Project var profiles []api.Profile var sourceInst *dbCluster.Instance 

Commit: f784961

lxd/instances_post: Backport source instance EntitlementCanView check to stable release branch

Commit: 1e3ffc5

incusd/instances: Check source permission on copy

--- a/cmd/incusd/instances_post.go +++ b/cmd/incusd/instances_post.go @@ -1228,6 +1229,20 @@ func instancesPost(d *Daemon, r *http.Request) response.Response { return response.BadRequest(errors.New("Target only allowed when clustered")) }  + // For a copy, check that the caller is allowed to view the source instance before any of its details are loaded. + if req.Source.Type == "copy" && req.Source.Source != "" { + sourceProject := req.Source.Project + if sourceProject == "" { + sourceProject = targetProjectName + } + + sourceName, _, _ := api.GetParentAndSnapshotName(req.Source.Source) + err = s.Authorizer.CheckPermission(r.Context(), r, auth.ObjectInstance(sourceProject, sourceName), auth.EntitlementCanView) + if err != nil { + return response.SmartError(err) + } + } +  err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error { dbProject, err := dbCluster.GetProject(ctx, tx.Tx(), targetProjectName) 

Exploit Details

Mitigation Strategies

  • Upgrade Canonical LXD or LXC Incus packages/snaps to versions containing the source instance authorization check.
  • Isolate untrusted multi-tenant workloads onto separate physical or virtual LXD/Incus host clusters rather than relying on shared daemon project restrictions.
  • Restrict network access to the LXD/Incus HTTPS API listener (port 8443) to trusted management networks.
  • Monitor daemon operation logs for cross-project instance copy operations.

Remediation Steps:

  1. For Snap-based LXD deployments on the 5.21 LTS track, run: snap refresh lxd --channel=5.21/stable (verify version >= 5.21.5).
  2. For Snap-based LXD deployments on the 5.0 LTS track, run: snap refresh lxd --channel=5.0/stable (verify version >= 5.0.7).
  3. For Snap-based LXD deployments on the 6.x feature track, run: snap refresh lxd --channel=latest/stable (verify version >= 6.8).
  4. For LXC Incus deployments, upgrade the incus package via your distribution package manager to version 7.2.0 or later.
  5. Audit existing projects using 'lxc list --all-projects' to verify whether any unauthorized duplicate instances were created in restricted user projects.

References

Read the full report for GHSA-QX75-2P3R-PWM5 on our website for more details including interactive diagrams and full exploit analysis.