CVE-2026-12411
Broken Access Control in Canonical LXD DevLXD API
Broken Access Control in the devLXDInstancePatchHandler component of Canonical LXD allows an untrusted guest to mount, read, and overwrite another guest's custom storage volume via a crafted device PATCH request over /dev/lxd when security.devlxd.management.volumes is enabled.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.4
Broken Access Control in Canonical LXD DevLXD API
Broken Access Control in the devLXDInstancePatchHandler component of Canonical LXD allows an untrusted guest to mount, read, and overwrite another guest's custom storage volume via a crafted device PATCH request over /dev/lxd when security.devlxd.management.volumes is enabled.
- Record
CVE-2026-12411- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
EUVDEUVD-2026-39788
8.4
lxd allows Cross-guest volume hijack via DevLXD device patch
- Record
EUVD-2026-39788- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Updated
- 2026-11-05
GitHub AdvisoriesGHSA-hhf9-qw4v-72xp
8.4
lxd allows Cross-guest volume hijack via DevLXD device patch
Summary
A guest can mount another guest's custom storage volume over /dev/lxd and read or write its contents without owning it. The DevLXD storage API blocks access to volumes you don't own, but the instance device-update path that attaches a volume to a container without checks ownership. Any guest in a project with security.devlxd.management.volumes enabled can attach a volume belonging to a different guest by naming it in a device PATCH, giving one tenant full read and write access to another tenant's volume data.
Details
Volumes created over DevLXD get volatile.devlxd.owner set to the calling identity. The storage handlers enforce it on GET/PUT/DELETE (isDevLXDVolumeOwner, lxd/devlxd_storage.go:359) and on volume-copy sources (devlxd_storage.go:286). The device-attach path does not.
devLXDInstancePatchHandler (lxd/devlxd_instances.go:88) passes the guest's device map to generateDevLXDInstanceDevices (devlxd_instances.go:178). For a device that isn't already on the instance, the only check is isDeviceAccessible(device):
if (exists && !isOwned) || (!isDeviceAccessible(device))
exists is false for a newly added device, so isDeviceAccessible is the whole gate. It resolves to newDevLXDDeviceAccessValidator (devlxd_instances.go:250), which is diskDeviceAllowed && filters.IsCustomVolumeDisk(device). IsCustomVolumeDisk (lxd/device/filters/disk.go:28) checks the device shape only: type=disk, non-empty source, non-empty pool. It never loads the (pool, source) volume or compares its owner to the caller.
The handler then sets volatile.<dev>.devlxd.owner to the caller and issues an in-process PATCH to instancePatch (lxd/instance_patch.go:55). That call skips the HTTP access handler and runs only project quota checks (limits.AllowInstanceUpdate, instance_patch.go:215) before c.Update. The disk device's validateConfig confirms the volume exists but does no ownership or RBAC check. A guest can therefore point a new disk device at any custom volume in its project and LXD mounts it. The recorded device owner becomes the attacker while the volume's volatile.devlxd.owner still names the victim.
PoC
Built from source at HEAD d29ece6284 (reports as 6.8, API extension devlxd_volume_management); single host, dir pool, default project.
Host setup, two guests with separate DevLXD identities, each scoped to edit only its own instance:
lxc launch images:alpine/edge guesta -c security.devlxd=true -c security.devlxd.management.volumes=true
lxc launch images:alpine/edge guestb -c security.devlxd=true -c security.devlxd.management.volumes=true
lxc exec guesta -- apk add --no-cache curl ; lxc exec guestb -- apk add --no-cache curl
for g in a b; do
lxc auth identity create devlxd/id$g
lxc auth group create g$g
lxc auth group permission add g$g project default can_view
lxc auth group permission add g$g project default can_view_storage_volumes
lxc auth group permission add g$g project default can_create_storage_volumes
lxc auth group permission add g$g instance guest$g can_view…
- Record
GHSA-hhf9-qw4v-72xp- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-05
NVD
8.4
Broken Access Control in the devLXDInstancePatchHandler component of Canonical LXD allows an untrusted guest to mount, read, and overwrite another guest's custom storage volume via a crafted device PATCH request over /dev/lxd when security.devlxd.management.volumes is enabled.
- Record
CVE-2026-12411- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - CVSS 3.1
- 9.6 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
OSV
8.4
Broken Access Control in Canonical LXD DevLXD API
Broken Access Control in the devLXDInstancePatchHandler component of Canonical LXD allows an untrusted guest to mount, read, and overwrite another guest's custom storage volume via a crafted device PATCH request over /dev/lxd when security.devlxd.management.volumes is enabled.
- Record
CVE-2026-12411- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-06
OSVGHSA-hhf9-qw4v-72xp
8.4
lxd allows Cross-guest volume hijack via DevLXD device patch
Summary
A guest can mount another guest's custom storage volume over /dev/lxd and read or write its contents without owning it. The DevLXD storage API blocks access to volumes you don't own, but the instance device-update path that attaches a volume to a container without checks ownership. Any guest in a project with security.devlxd.management.volumes enabled can attach a volume belonging to a different guest by naming it in a device PATCH, giving one tenant full read and write access to another tenant's volume data.
Details
Volumes created over DevLXD get volatile.devlxd.owner set to the calling identity. The storage handlers enforce it on GET/PUT/DELETE (isDevLXDVolumeOwner, lxd/devlxd_storage.go:359) and on volume-copy sources (devlxd_storage.go:286). The device-attach path does not.
devLXDInstancePatchHandler (lxd/devlxd_instances.go:88) passes the guest's device map to generateDevLXDInstanceDevices (devlxd_instances.go:178). For a device that isn't already on the instance, the only check is isDeviceAccessible(device):
if (exists && !isOwned) || (!isDeviceAccessible(device))
exists is false for a newly added device, so isDeviceAccessible is the whole gate. It resolves to newDevLXDDeviceAccessValidator (devlxd_instances.go:250), which is diskDeviceAllowed && filters.IsCustomVolumeDisk(device). IsCustomVolumeDisk (lxd/device/filters/disk.go:28) checks the device shape only: type=disk, non-empty source, non-empty pool. It never loads the (pool, source) volume or compares its owner to the caller.
The handler then sets volatile.<dev>.devlxd.owner to the caller and issues an in-process PATCH to instancePatch (lxd/instance_patch.go:55). That call skips the HTTP access handler and runs only project quota checks (limits.AllowInstanceUpdate, instance_patch.go:215) before c.Update. The disk device's validateConfig confirms the volume exists but does no ownership or RBAC check. A guest can therefore point a new disk device at any custom volume in its project and LXD mounts it. The recorded device owner becomes the attacker while the volume's volatile.devlxd.owner still names the victim.
PoC
Built from source at HEAD d29ece6284 (reports as 6.8, API extension devlxd_volume_management); single host, dir pool, default project.
Host setup, two guests with separate DevLXD identities, each scoped to edit only its own instance:
lxc launch images:alpine/edge guesta -c security.devlxd=true -c security.devlxd.management.volumes=true
lxc launch images:alpine/edge guestb -c security.devlxd=true -c security.devlxd.management.volumes=true
lxc exec guesta -- apk add --no-cache curl ; lxc exec guestb -- apk add --no-cache curl
for g in a b; do
lxc auth identity create devlxd/id$g
lxc auth group create g$g
lxc auth group permission add g$g project default can_view
lxc auth group permission add g$g project default can_view_storage_volumes
lxc auth group permission add g$g project default can_create_storage_volumes
lxc auth group permission add g$g instance guest$g can_view…
- Record
GHSA-hhf9-qw4v-72xp- CVSS 3.1
- 8.4 High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-06
OSVGO-2026-6917 No score
LXD allows cross-guest volume hijack via DevLXD device patch in github.com/canonical/lxd
LXD allows cross-guest volume hijack via DevLXD device patch in github.com/canonical/lxd
- Record
GO-2026-6917- Updated
- 2026-11-06
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/canonical/lxdGo |
0.0.0-20250721152936-fe86ceb2c3fa up to 0.0.0-20260626085133-7cb96ac80a6d | 0.0.0-20260626085133-7cb96ac80a6d |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases, references | OSV | |
| published | EUVD | |
| affected, facts, references | GitHub Advisories, OSV | |
| first indexed | CVE List, EUVD, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-12411 is a high vulnerability (CVSS 8.4) in Canonical Lxd, published June 26, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.24%, the 14th percentile. Fixed in github.com/canonical/lxd 0.0.0-20260626085133-7cb96ac80a6d.
References
github.com/canonical/lxd/security/advisories/GHSA-hhf9-qw4v-72xp
github.com/canonical/lxd/pull/18585
nvd.nist.gov/vuln/detail/CVE-2026-12411
github.com/canonical/lxd/commit/752e25cfb849a349ff08939ce02747ef4d4e69cc
github.com/advisories/GHSA-hhf9-qw4v-72xp
github.com/canonical
github.com/CVEProject/cvelistV5/tree/main/cves/2026/12xxx/CVE-2026-12411.json
github.com/canonical/lxd
github.com/canonical/lxd/commit/7cb96ac80a6d52a96b53b0ae06a8659606fa8dd8
Left out here: the rest of 2 source descriptions. The JSON record has them.