Nov 5, 2026·10 min read·1 visit
An authorization bypass in Canonical LXD (versions 6.6 to 6.8) allows a guest instance with DevLXD volume management enabled to mount, read, and modify custom storage volumes belonging to other guests or identities by submitting a crafted device PATCH request over the /dev/lxd socket.
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.
Canonical LXD provides a system container and virtual machine manager built on top of Linux containers (LXC) and QEMU. To facilitate guest-to-host communication without requiring network access to the primary LXD daemon API, LXD exposes a Unix domain socket at /dev/lxd inside guest instances. This interface, known as DevLXD, allows guests to query instance metadata, retrieve configuration properties, and—when specific security flags are enabled—manage guest-specific resources such as custom storage volumes.
In LXD 6.6, Canonical introduced expanded DevLXD capabilities allowing guests to self-manage custom storage volumes when the instance configuration key security.devlxd.management.volumes is set to true. Access control within this subsystem relies on fine-grained DevLXD identities (devlxd/<identity-name>) authenticated via bearer tokens. Each custom storage volume created or managed through DevLXD records its owning identity within the volume metadata key volatile.devlxd.owner.
CVE-2026-12411 (classified under CWE-639 and CWE-862) is an authorization bypass in the devLXDInstancePatchHandler function within lxd/devlxd_instances.go. While the DevLXD storage volume endpoints properly enforce ownership checks when querying or modifying volumes directly, the instance device management endpoint failed to verify whether the requesting identity owned the underlying storage volume referenced in a new disk device attachment.
Consequently, an authenticated DevLXD identity with permission to edit its host instance can submit an HTTP PATCH request to /dev/lxd that defines a new disk device pointing to an arbitrary custom volume name and storage pool within the same project. The LXD daemon processes the device addition and mounts the target volume into the guest filesystem, bypassing tenant isolation boundaries and granting the caller complete read and write access to the victim volume's contents.
The root cause of CVE-2026-12411 is an incomplete authorization check during the evaluation of instance device updates in generateDevLXDInstanceDevices. When a guest submits an HTTP PATCH request to update its device configuration via /dev/lxd, the request is routed to devLXDInstancePatchHandler. This handler extracts the caller's identity (requestor.Username) and invokes generateDevLXDInstanceDevices to validate the requested device modifications and compute the updated instance configuration.
In vulnerable versions of LXD (6.6 through 6.8), generateDevLXDInstanceDevices iterated through each device in the incoming request payload (req.Devices) and checked whether the device name already existed in the instance's expanded device map (inst.ExpandedDevices[name]). The authorization guard was implemented as if (exists && !isOwned) || (!isDeviceAccessible(device)). Here, isOwned verified whether an existing attached device belonged to the caller by inspecting volatile.<device-name>.devlxd.owner in the instance configuration.
When an attacker attached a new device under an unused device key, exists evaluated to false, completely bypassing the !isOwned condition. The security check then relied exclusively on isDeviceAccessible(device), a closure generated by newDevLXDDeviceAccessValidator(inst). However, newDevLXDDeviceAccessValidator only verified two static properties: whether security.devlxd.management.volumes was enabled on the instance (diskDeviceAllowed), and whether the submitted device map syntactically matched a custom volume disk definition (filters.IsCustomVolumeDisk(device)).
Because neither generateDevLXDInstanceDevices nor isDeviceAccessible queried the LXD database to verify the volatile.devlxd.owner property of the target storage volume (device["source"] inside device["pool"]), the authorization check succeeded unconditionally for any valid custom volume name. Furthermore, once the check passed, LXD recorded the new device attachment in the instance configuration as owned by the attacker (newConfig["volatile."+name+".devlxd.owner"] = identityID) and instructed the host storage driver to mount the volume into the guest.
The vulnerability and its remediation are located in lxd/devlxd_instances.go. In the vulnerable implementation prior to LXD 6.9, newDevLXDDeviceAccessValidator returned a boolean function that inspected only instance-level feature flags and device map fields without database or context awareness.
// Vulnerable implementation in lxd/devlxd_instances.go (LXD 6.6 - 6.8)
type devLXDDeviceAccessValidator func(device map[string]string) bool
func newDevLXDDeviceAccessValidator(inst instance.Instance) devLXDDeviceAccessValidator {
diskDeviceAllowed := hasInstanceSecurityFeatures(inst.ExpandedConfig(), devLXDSecurityManagementVolumesKey)
return func(device map[string]string) bool {
// Only verifies instance config flag and device type/syntax
return diskDeviceAllowed && filters.IsCustomVolumeDisk(device)
}
}
func generateDevLXDInstanceDevices(inst api.Instance, req api.DevLXDInstancePut, identityID string, isDeviceAccessible devLXDDeviceAccessValidator) (devices map[string]map[string]string, config map[string]string, err error) {
// ...
for name, device := range req.Devices {
// ...
_, exists := inst.ExpandedDevices[name]
// BUG: If exists is false (new device attachment), !isOwned is not evaluated,
// and isDeviceAccessible does not check who owns device["source"].
if (exists && !isOwned) || (!isDeviceAccessible(device)) {
return nil, nil, api.StatusErrorf(http.StatusForbidden, "Not authorized to manage device %q", name)
}
// ...
}
}In commit 7cb96ac80a6d52a96b53b0ae06a8659606fa8dd8, Canonical remediated the flaw by modifying devLXDDeviceAccessValidator to return an error and introducing a second validator function, newDevLXDDeviceResourceOwnershipValidator. This new validator is passed into generateDevLXDInstanceDevices as isDeviceResourceOwned.
// Patched implementation in lxd/devlxd_instances.go (LXD 6.9+)
type devLXDDeviceAccessValidator func(device map[string]string) error
func devLXDInstancePatchHandler(d *Daemon, r *http.Request) response.Response {
// ...
deviceChecker := newDevLXDDeviceAccessValidator(inst)
deviceResourceOwnershipChecker := newDevLXDDeviceResourceOwnershipValidator(r.Context(), d, projectName)
devices, config, err := generateDevLXDInstanceDevices(targetInst, reqInst, requestor.Username, deviceChecker, deviceResourceOwnershipChecker)
if err != nil {
return response.DevLXDErrorResponse(err)
}
// ...
}
func newDevLXDDeviceResourceOwnershipValidator(ctx context.Context, d *Daemon, projectName string) devLXDDeviceAccessValidator {
return func(device map[string]string) error {
poolName := device["pool"]
volName := device["source"]
if poolName == "" || volName == "" {
return api.StatusErrorf(http.StatusBadRequest, "Invalid device configuration: Missing pool or volume name")
}
// Construct internal API request to resolve storage volume details within the caller's context
url := api.NewURL().Path("1.0", "storage-pools", poolName, "volumes", "custom", volName).Project(projectName)
req, err := lxd.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil, "")
if err != nil {
return api.StatusErrorf(http.StatusInternalServerError, "Failed creating request: %w", err)
}
req.SetPathValue("type", "custom")
req.SetPathValue("poolName", poolName)
req.SetPathValue("volumeName", volName)
err = addStoragePoolVolumeDetailsToRequestContext(d.State(), req)
if err != nil {
return err
}
// Retrieve volume via devLXDStoragePoolVolumeGet to enforce volatile.devlxd.owner verification
_, _, err = devLXDStoragePoolVolumeGet(req.Context(), d, "", projectName, poolName, volName, "custom")
if err != nil {
return err
}
return nil
}
}The fix is architectural and complete for custom volume disk attachments. Rather than duplicating ownership logic, newDevLXDDeviceResourceOwnershipValidator synthesizes an internal HTTP GET request carrying the caller's r.Context() (which contains the authenticated DevLXD identity) and delegates verification to devLXDStoragePoolVolumeGet. If the target volume does not exist or its volatile.devlxd.owner metadata does not match the requesting identity, devLXDStoragePoolVolumeGet returns an error, halting device generation before any host storage operations occur.
Exploiting CVE-2026-12411 requires local execution inside an LXD container or virtual machine where security.devlxd.management.volumes=true is configured. Additionally, the attacker must possess a valid DevLXD bearer token associated with an identity (devlxd/<identity>) that has been granted can_edit permissions on the instance and storage_volume_manager permissions within the project. The attacker also needs to know or guess the target storage pool name and the victim's custom volume name.
In a multi-tenant or shared identity scenario, suppose a legitimate identity (devlxd/victim-identity) creates a custom storage volume named victim-secret-vol in the pool test-pool over /dev/lxd. LXD sets volatile.devlxd.owner: devlxd/victim-identity on victim-secret-vol. When a second, untrusted identity (devlxd/attacker-identity) attempts to read or delete victim-secret-vol through the /dev/lxd/1.0/storage-pools/test-pool/volumes/custom/victim-secret-vol endpoint, LXD rejects the request.
To bypass this restriction, devlxd/attacker-identity targets the instance configuration endpoint via an HTTP PATCH request over /dev/lxd (or by invoking the devlxd-client instance update utility). The attacker submits a JSON payload defining a new disk device (e.g., hijacked-vol) that specifies pool: test-pool, source: victim-secret-vol, and an arbitrary guest mount path such as /mnt/hijacked-secret-data.
# 1. Construct the malicious device PATCH payload targeting the victim's volume
ATTACK_PAYLOAD=$(cat <<EOF
{
"devices": {
"hijacked-vol": {
"type": "disk",
"pool": "test-pool",
"source": "victim-secret-vol",
"path": "/mnt/hijacked-secret-data"
}
}
}
EOF
)
# 2. Submit the PATCH request over /dev/lxd using the attacker's bearer token
lxc exec guest-attacker --project test-project --env DEVLXD_BEARER_TOKEN="${TOKEN_ATTACKER}" -- \
devlxd-client instance update guest-attacker "${ATTACK_PAYLOAD}"Upon receiving this payload, a vulnerable LXD daemon (< 6.9) verifies that hijacked-vol is a new device key on guest-attacker and confirms that the JSON structure matches a custom disk volume. It then attaches victim-secret-vol to guest-attacker and mounts it at /mnt/hijacked-secret-data, granting processes inside the guest immediate filesystem-level read and write access to the victim's volume.
CVE-2026-12411 carries a CVSS v3.1 base score of 8.4 (High) with the vector CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N. The Attack Vector is Local (AV:L) and Privileges Required is Low (PR:L) because exploitation originates from inside an allocated guest instance using a provisioned DevLXD token. Attack Complexity is Low (AC:L) and requires no user interaction (UI:N), as the attack relies on a single deterministic API request.
The Scope is Changed (S:C) because the vulnerability resides in the host-managed LXD daemon and allows an untrusted guest identity to cross security boundaries and access storage resources belonging to other tenants or identities within the same LXD project. Both Confidentiality (C:H) and Integrity (I:H) impacts are High.
Once the unauthorized custom volume is mounted into the attacker's guest instance, the attacker can read sensitive application data, credentials, or keys stored on the volume. Furthermore, because the volume is mounted read-write by default, the attacker can modify, corrupt, or delete files on the volume, directly compromising the integrity of any other instance relying on that storage volume.
> [!NOTE]
> While the CVSS Availability metric is rated None (A:N) with respect to the host daemon itself, an attacker with write access to the mounted filesystem can delete all data on the victim volume, causing data loss and service disruption for the legitimate volume owner.
According to FIRST EPSS metrics, CVE-2026-12411 has an exploitation probability score of 0.00288 (21.48th percentile) and is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. The prerequisite that security.devlxd.management.volumes must be explicitly enabled limits exposure on default deployments, concentrating risk in cloud or container hosting environments that delegate storage volume management to guest instances.
Canonical addressed CVE-2026-12411 in LXD version 6.9 via pull request #18585 and commit 7cb96ac80a6d52a96b53b0ae06a8659606fa8dd8. Administrators running LXD versions 6.6, 6.7, or 6.8 should upgrade to LXD 6.9 or later immediately. On systems where LXD is deployed via Snap, the daemon can be updated using the standard channel refresh command:
sudo snap refresh lxd --channel=latest/stableIf an immediate upgrade is not operationally feasible, administrators can mitigate the vulnerability by disabling DevLXD volume self-management across all instances. Removing or setting security.devlxd.management.volumes to false causes newDevLXDDeviceAccessValidator to reject all disk device modification requests originating from /dev/lxd.
# Audit all instances across projects to identify where volume management is enabled
lxc list -c n,c -f json | jq '.[] | select(.config["security.devlxd.management.volumes"] == "true") | .name'
# Disable DevLXD volume management on a vulnerable instance
lxc config unset <instance-name> security.devlxd.management.volumesIn addition to disabling the configuration key, security teams should review active DevLXD authentication identities and revoke unnecessary can_edit instance permissions or storage_volume_manager project permissions. Administrators should also audit existing instance configurations for unexpected volatile.<device-name>.devlxd.owner entries and cross-reference attached disk source values against the volatile.devlxd.owner metadata of the corresponding custom storage volumes to detect prior unauthorized attachments.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
LXD Canonical | >= 6.6, < 6.9 | 6.9 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-12411 (GHSA-hhf9-qw4v-72xp) |
| Weakness Class | CWE-639 (Authorization Bypass Through User-Controlled Key) / CWE-862 |
| Affected Versions | Canonical LXD >= 6.6, < 6.9 |
| Attack Vector | Local via /dev/lxd socket (AV:L/AC:L/PR:L/UI:N) |
| CVSS v3.1 Score | 8.4 High (Scope Changed, High Confidentiality & Integrity Impact) |
| EPSS Score | 0.29% (0.00288 - 21.48th percentile) |
| Exploit Status | Functional PoC in upstream test suite (Not in CISA KEV) |
| Patched Version | Canonical LXD 6.9 |
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
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.
CVE-2026-61536 is a high-severity Remote Code Execution (RCE) vulnerability in the masci/banks Python library prior to version 2.4.3. The flaw exists within the CompletionExtension component, which parses JSON-serialized Tool definitions directly from rendered Jinja template bodies inside {% completion %} blocks. When handling tool calls returned by an LLM, the library dynamically resolves the target callable using importlib.import_module() and getattr() based on an unvalidated import_path string. An attacker who can supply or modify prompt templates can inject a malicious Tool JSON object pointing to arbitrary Python functions (such as os.system), bypassing the Jinja SandboxedEnvironment introduced in version 2.4.2 and achieving arbitrary code execution when the LLM invokes the tool.
Payload CMS contains a missing authorization vulnerability (CWE-862) in its client-side upload instructions endpoint and the `@payloadcms/storage-vercel-blob` storage adapter. Two distinct logic defects allow authenticated users lacking collection-level `create` or `update` permissions to generate signed upload tokens and upload arbitrary files directly to external cloud storage.
CVE-2026-55098 (GHSA-93cm-2v4m-c56c) is an authorization bypass and sensitive information disclosure vulnerability in Basekick-Labs Arc prior to version 26.06.2. The flaw stems from an incomplete remediation of CVE-2026-47735, which failed to restrict the full suite of DuckDB table-valued filesystem I/O functions and quoted-identifier invocations. Because Arc's query parser ignores function calls during table reference extraction, authenticated users with minimal or empty permissions can invoke functions like parquet_scan(), glob(), and read_blob() to read, enumerate, and extract telemetry data across all tenant databases residing within the shared local storage root.
CVE-2026-113787 is a stored Cross-Site Scripting (XSS) and HTML injection vulnerability affecting the odf-kit npm package from version 0.6.0 prior to 0.14.2. The HTML conversion engines (odtToHtml and odsToHtml) interpolate document-controlled styling and image attributes directly into inline style and src attributes without escaping double-quote characters. A remote attacker can supply a crafted .odt or .ods document to break out of HTML attribute boundaries and execute arbitrary JavaScript in the context of any web application rendering the converted HTML.