Dev.to Security ๐Ÿ” Cybersecurity ๐Ÿ‘ 0 ๐Ÿ“– 4 min read

GHSA-JPF8-86F3-WP38: CVE-2026-48752: Arbitrary Host File Read and Write via Templates Symlink in Canonical LXD and Incus

CVE-2026-48752: Arbitrary Host File Read and Write via Templates Symlink in Canonical LXD and Incus Vulnerability ID: GHSA-JPF8-86F3-WP38 CVSS Score: 9.9 Published: 2026-11-06 A symbolic link traversal vulnerability

CVE-2026-48752: Arbitrary Host File Read and Write via Templates Symlink in Canonical LXD and Incus

Vulnerability ID: GHSA-JPF8-86F3-WP38
CVSS Score: 9.9
Published: 2026-11-06

A symbolic link traversal vulnerability (CWE-59) in Canonical LXD and Linux Containers Incus allows an authenticated user with image import permissions to read and write arbitrary files on the host filesystem. During image archive unpacking, the daemon fails to reject top-level symbolic links for the instance templates directory. Subsequent requests to the instance template API endpoints follow this symbolic link outside the instance storage directory with root privileges, enabling complete host compromise.

TL;DR

Authenticated users capable of importing images into LXD or Incus can include a symbolic link at the templates path inside an image tarball. Interacting with the instance template management API causes the root daemon to traverse this symlink onto the host filesystem, granting arbitrary file read and write access as root.

โš ๏ธ Exploit Status: POC

Technical Details

  • CVE ID: CVE-2026-48752
  • Primary CWE: CWE-59: Improper Link Resolution Before File Access ('Link Following')
  • CVSS v3.1 Score: 9.9 Critical (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
  • Attack Vector: Network / Local UNIX Socket API (Authenticated Image Import)
  • Security Impact: Arbitrary Host File Read & Write as Root / Container Escape
  • EPSS Score: 0.78% (56.22nd Percentile)
  • Exploit Status: Public Proof-of-Concept Available
  • CISA KEV Status: Not Listed

Affected Systems

  • Canonical LXD
  • Linux Containers Incus
  • LXD (4.0 LTS): >= 4.0.0, < 4.0.11 (Fixed in: 4.0.11)
  • LXD (5.0 LTS): >= 5.0.0, < 5.0.7 (Fixed in: 5.0.7)
  • LXD (5.x Feature Releases): >= 5.1.0, < 5.21.5 (Fixed in: 5.21.5)
  • LXD (6.x): >= 6.0.0, < 6.9.0 (Fixed in: 6.9.0)
  • Incus: < 7.2.0 (Fixed in: 7.2.0)

Code Analysis

Commit: 190e245

lxd/instance/drivers/common: Prevent rootfs or templates dir to be a symlink

diff --git a/lxd/instance/drivers/driver_common.go b/lxd/instance/drivers/driver_common.go
index 8bd1c424d81d..eb33a73d2619 100644
--- a/lxd/instance/drivers/driver_common.go
+++ b/lxd/instance/drivers/driver_common.go
@@ -3,6 +3,7 @@ package drivers
 import (
    "database/sql"
    "fmt"
+   "os"
    "path/filepath"
    "strconv"
    "strings"
@@ -321,8 +322,9 @@ func (d *common) Path() string {
 }

 // RootfsPath returns the instance's rootfs path.
-func (d *common) RootfsPath() string {
-   return filepath.Join(d.Path(), "rootfs")
+// Returns an error if the path exists and is a symlink.
+func (d *common) RootfsPath() (string, error) {
+   return d.subPath("rootfs")
 }

 // ShmountsPath returns the instance's shared mounts path.
@@ -337,8 +339,29 @@ func (d *common) StatePath() string {
 }

 // TemplatesPath returns the instance's templates path.
-func (d *common) TemplatesPath() string {
-   return filepath.Join(d.Path(), "templates")
+// Returns an error if the path exists and is a symlink.
+func (d *common) TemplatesPath() (string, error) {
+   return d.subPath("templates")
+}
+
+// subPath returns the path of name beneath the instance's path, rejecting it if it exists and is a symlink.
+func (d *common) subPath(name string) (string, error) {
+   path := filepath.Join(d.Path(), name)
+
+   fi, err := os.Lstat(path)
+   if err != nil {
+       if os.IsNotExist(err) {
+           return path, nil
+       }
+
+       return "", err
+   }
+
+   if fi.Mode()&os.ModeSymlink != 0 {
+       return "", fmt.Errorf("%q for instance %q is a symlink", name, d.name)
+   }
+
+   return path, nil
 }

Commit: 254b85c

lxd/instance_metadata: Confine opening of templates

Commit: 4cd6334

lxd/instance_metadata: Confine opening of templates

Commit: 734fcc4

lxd/instance_metadata: Confine opening of templates

Commit: 79f3879

lxd/instance_metadata: Confine opening of templates

Commit: cbefa31

incusd/instance: Confine template access to instance root (addresses CVE-2026-48752)

Exploit Details

Mitigation Strategies

  • Upgrade Canonical LXD or Linux Containers Incus to a patched release that enforces directory confinement using os.OpenRoot.
  • Restrict API access, local UNIX socket group memberships (lxd/incus-admin), and TLS client certificate trust stores to trusted system administrators.
  • Audit host storage pool directories for existing instances containing symbolic links named 'templates'.
  • Monitor host file integrity in sensitive directories such as /etc/cron.d, /etc/systemd/system, and /root/.ssh.

Remediation Steps:

  1. For Linux Containers Incus, upgrade the incus package to version 7.2.0 or later using your distribution package manager.
  2. For Canonical LXD, refresh the snap package or upgrade to version 4.0.11, 5.0.7, 5.21.5, or 6.9.0+ depending on the active release channel.
  3. Execute find /var/snap/lxd/common/lxd/storage-pools/ -maxdepth 4 -name templates -type l (LXD) or find /var/lib/incus/storage-pools/ -maxdepth 4 -name templates -type l (Incus) to identify any maliciously crafted instances.
  4. Review daemon access logs for anomalous GET or POST requests to /1.0/instances/<instance>/metadata/templates.

References

Read the full report for GHSA-JPF8-86F3-WP38 on our website for more details including interactive diagrams and full exploit analysis.

๐Ÿ“ฐ Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes โ€” full credit and traffic to the original publisher.