LXD: VM lowlevel restriction bypass via raw.apparmor and raw.qemu.conf
Date: 2026-04
Affected: go/github.com/canonical/lxd
Attack vector: other
Category: vulnerability-disclosure
Severity: Critical
CVE: CVE-2026-34177
CWE: CWE-184
CVSS: 9.1 (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)

## Summary The `isVMLowLevelOptionForbidden` function in `lxd/project/limits/permissions.go` is missing `raw.apparmor` and `raw.qemu.conf` from its hardcoded forbidden list. A user with `can_edit` permission on a VM instance in a restricted project can combine these two omissions to bridge the LXD unix socket into the guest VM and gain full cluster administrator access. This bypasses the `restricted.virtual-machines.lowlevel=block` project restriction, which is the security control specifically designed to prevent raw config injection. ## Details ### Affected code The enforcement point for VM lowlevel restrictions is `isVMLowLevelOptionForbidden` at `lxd/project/limits/permissions.go:924-926`: ```go func isVMLowLevelOptionForbidden(key string) bool { return slices.Contains([]string{"boot.host_shutdown_timeout", "limits.memory.hugepages", "raw.idmap", "raw.qemu"}, key) } ``` This list is missing two security-critical config keys: - **`raw.apparmor`** -- allows injecting arbitrary AppArmor rules into the QEMU process confinement profile - **`raw.qemu.conf`** -- allows injecting arbitrary sections into the QEMU configuration file The container equivalent (`isContainerLowLevelOptionForbidden` at line 916) correctly includes `raw.apparmor` in its forbidden list. ### Attack mechanism Both `raw.apparmor` and `raw.qemu.conf` are valid VM config keys (defined in `lxd/instance/instancetype/instance.go`). When a restricted user sets them on a VM in a project with `restrict


Mitigations:

Architectural layers (MAESTRO):