GHSA-QX75-2P3R-PWM5: CVE-2026-55622: Cross-Project Instance Copy Authorization Bypass in Canonical LXD and LXC Incus
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
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
- GitHub Security Advisory (GHSA-qx75-2p3r-pwm5): Official Canonical LXD advisory containing technical reproduction steps and Python proof-of-concept exploit script.
- GitHub Security Advisory (GHSA-c9f5-j9c3-mhrg): Official LXC Incus advisory detailing the cross-project instance copy vulnerability.
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:
- For Snap-based LXD deployments on the 5.21 LTS track, run: snap refresh lxd --channel=5.21/stable (verify version >= 5.21.5).
- For Snap-based LXD deployments on the 5.0 LTS track, run: snap refresh lxd --channel=5.0/stable (verify version >= 5.0.7).
- For Snap-based LXD deployments on the 6.x feature track, run: snap refresh lxd --channel=latest/stable (verify version >= 6.8).
- For LXC Incus deployments, upgrade the incus package via your distribution package manager to version 7.2.0 or later.
- Audit existing projects using 'lxc list --all-projects' to verify whether any unauthorized duplicate instances were created in restricted user projects.
References
- Canonical LXD Security Advisory (GHSA-qx75-2p3r-pwm5)
- LXC Incus Security Advisory (GHSA-c9f5-j9c3-mhrg)
- National Vulnerability Database (CVE-2026-55622)
- CVE.org Record (CVE-2026-55622)
- Canonical LXD Fix Commit (97cb037794cb6b21c5a184eb5f3292207adbf6b2)
- Canonical LXD LTS Fix Commit (bc1e20ea32dff6ae2df08803eee82832959ec80e)
- Canonical LXD Fix Commit (f7849615eb89e71a12ea76daaab6110bdf0fdf7f)
- LXC Incus Pull Request #3542
- LXC Incus Fix Commit (1e3ffc53a10950e55de62ac1e0d612be597b84eb)
- Incus 7.2 Release Announcement
Read the full report for GHSA-QX75-2P3R-PWM5 on our website for more details including interactive diagrams and full exploit analysis.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.