--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
User: [email protected]
Usertags: pu
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:incus
[ Reason ]
Last week's release of Incus 7.4 included fixes for two moderate
severity issues, CVE-2026-81500 and CVE-2026-81501. After discussion
with the Security Team, these vulnerabilities won't receive their own
DSA, but will be addressed via the upcoming point release.
[ Impact ]
Incus in trixie is currently vulnerable to CVE-2026-81500 and CVE-2026-
81501.
[ Tests ]
None -- both security issues are somewhat obscure edge cases, but the
fixes have been in the stable release for a week now and no regressions
have been reported upstream.
[ Risks ]
Minor/none -- two targeted fixes cherry-picked from the upstream git
repo.
[ Checklist ]
[*] *all* changes are documented in the d/changelog
[*] I reviewed all changes and I approve them
[*] attach debdiff against the package in (old)stable
[*] the issue is verified as fixed in unstable
[ Changes ]
Two security fixes as outlined above. Also updated d/changelog with
missing CVEs that hadn't been assigned by GitHub when the previous
release was uploaded.
[ Other info ]
The source debdiff is attached.
diff --git a/debian/changelog b/debian/changelog
index 438c618e46..8cc80a7c1f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+incus (6.0.4-2+deb13u10) trixie; urgency=medium
+
+ * Cherry-pick fixes for the following security issues
+ - CVE-2026-81500 / GHSA-9pqw-c7m4-xvg7
+ - CVE-2026-81501 / GHSA-c6wx-8679-hpr9
+
+ -- Mathias Gibbens <[email protected]> Wed, 02 Sep 2026 16:39:29 +0000
+
incus (6.0.4-2+deb13u9) trixie-security; urgency=high
* Cherry-pick upstream fix for large nft ruleset performance
@@ -8,11 +16,11 @@ incus (6.0.4-2+deb13u9) trixie-security; urgency=high
- CVE-2026-62941 / GHSA-mq9x-prm8-3vpw
- CVE-2026-63125 / GHSA-6rqx-22hc-qm36
- CVE-2026-63343 / GHSA-fmjx-5j3g-997p
- - GHSA-26gp-p5fw-3r2h
- - GHSA-4qxq-p5hm-3q3p
- - GHSA-67qw-68v3-36h6
- - GHSA-m3j6-p3v3-qmjv
- - GHSA-p2v3-6wvc-cv3p
+ - CVE-2026-81493 / GHSA-p2v3-6wvc-cv3p
+ - CVE-2026-81495 / GHSA-67qw-68v3-36h6
+ - CVE-2026-81496 / GHSA-26gp-p5fw-3r2h
+ - CVE-2026-81497 / GHSA-4qxq-p5hm-3q3p
+ - CVE-2026-81498 / GHSA-m3j6-p3v3-qmjv
* Cherry-pick four additional security fixes not assigned CVEs
-- Mathias Gibbens <[email protected]> Thu, 30 Jul 2026 22:57:51 +0000
diff --git a/debian/patches/147-CVE-2026-81500.patch b/debian/patches/147-CVE-2026-81500.patch
new file mode 100644
index 0000000000..217f0de519
--- /dev/null
+++ b/debian/patches/147-CVE-2026-81500.patch
@@ -0,0 +1,91 @@
+From d4d7badf6597274320b78575e77aec5720163c9c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?St=C3=A9phane=20Graber?= <[email protected]>
+Date: Sun, 23 Aug 2026 17:26:26 -0400
+Subject: [PATCH] client/images: Prevent path traversal in downloaded image
+ name
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The local filename for an exported image came from server-controlled
+data (Content-Disposition for unified images, the simplestreams index
+path) and was joined with the target directory. Basename it.
+
+This addresses GHSA-9pqw-c7m4-xvg7 (CVE pending)
+
+Signed-off-by: Stéphane Graber <[email protected]>
+Rebased-by: Mathias Gibbens <[email protected]>
+---
+ client/incus_images.go | 4 +++-
+ client/simplestreams_images.go | 13 +++++++------
+ 2 files changed, 10 insertions(+), 7 deletions(-)
+
+diff --git a/client/incus_images.go b/client/incus_images.go
+index ed48d08be..3a0afccb2 100644
+--- a/client/incus_images.go
++++ b/client/incus_images.go
+@@ -9,6 +9,7 @@ import (
+ "net/http"
+ "net/url"
+ "os"
++ "path/filepath"
+ "slices"
+ "strings"
+ "time"
+@@ -325,7 +326,8 @@ func incusDownloadImage(fingerprint string, uri string, userAgent string, do fun
+ }
+
+ resp.MetaSize = size
+- resp.MetaName = filename
++ // Basename the server-provided name to prevent path traversal.
++ resp.MetaName = filepath.Base(filename)
+
+ // Check the hash
+ hash := fmt.Sprintf("%x", sha256.Sum(nil))
+diff --git a/client/simplestreams_images.go b/client/simplestreams_images.go
+index 00cc35409..6bbfbe9d4 100644
+--- a/client/simplestreams_images.go
++++ b/client/simplestreams_images.go
+@@ -10,6 +10,7 @@ import (
+ "net/url"
+ "os"
+ "os/exec"
++ "path/filepath"
+ "strings"
+ "time"
+
+@@ -143,8 +144,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe
+ return nil, err
+ }
+
+- parts := strings.Split(meta.Path, "/")
+- resp.MetaName = parts[len(parts)-1]
++ // Basename the server-provided name to prevent path traversal.
++ resp.MetaName = filepath.Base(meta.Path)
+ resp.MetaSize = size
+ }
+
+@@ -205,8 +206,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe
+ return nil, err
+ }
+
+- parts := strings.Split(rootfs.Path, "/")
+- resp.RootfsName = parts[len(parts)-1]
++ // Basename the server-provided name to prevent path traversal.
++ resp.RootfsName = filepath.Base(rootfs.Path)
+ resp.RootfsSize = size
+ downloaded = true
+ }
+@@ -219,8 +220,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe
+ return nil, err
+ }
+
+- parts := strings.Split(rootfs.Path, "/")
+- resp.RootfsName = parts[len(parts)-1]
++ // Basename the server-provided name to prevent path traversal.
++ resp.RootfsName = filepath.Base(rootfs.Path)
+ resp.RootfsSize = size
+ }
+ }
+--
+2.47.3
diff --git a/debian/patches/148-CVE-2026-81501.patch b/debian/patches/148-CVE-2026-81501.patch
new file mode 100644
index 0000000000..f05a56fb8b
--- /dev/null
+++ b/debian/patches/148-CVE-2026-81501.patch
@@ -0,0 +1,115 @@
+From a04abf23169d0597a544c6d96044a0f7aa9f19bf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?St=C3=A9phane=20Graber?= <[email protected]>
+Date: Sun, 23 Aug 2026 17:26:26 -0400
+Subject: [PATCH] incusd/images: Check access before reusing cross-project
+ image
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+imageDownload reused an image from another project without checking the
+caller could view it, letting a client that knew a private fingerprint
+import it. Only reuse it directly when public or viewable, otherwise
+download it (proving access) and dedupe against the on-disk copy.
+
+This addresses GHSA-c6wx-8679-hpr9 (CVE pending)
+
+Signed-off-by: Stéphane Graber <[email protected]>
+Rebased-by: Mathias Gibbens <[email protected]>
+---
+ cmd/incusd/daemon_images.go | 38 +++++++++++++++++++++++++++++++-----
+ internal/server/db/images.go | 1 +
+ 2 files changed, 34 insertions(+), 5 deletions(-)
+
+diff --git a/cmd/incusd/daemon_images.go b/cmd/incusd/daemon_images.go
+index 8eafb6de4..c26e1540e 100644
+--- a/cmd/incusd/daemon_images.go
++++ b/cmd/incusd/daemon_images.go
+@@ -14,6 +14,7 @@ import (
+
+ incus "github.com/lxc/incus/v6/client"
+ internalIO "github.com/lxc/incus/v6/internal/io"
++ "github.com/lxc/incus/v6/internal/server/auth"
+ "github.com/lxc/incus/v6/internal/server/db"
+ "github.com/lxc/incus/v6/internal/server/db/cluster"
+ "github.com/lxc/incus/v6/internal/server/locking"
+@@ -195,13 +196,37 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope
+ }
+ }
+ } else if response.IsNotFoundError(err) {
++ var otherImg *api.Image
+ err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
+ // Check if the image already exists in some other project.
+- _, imgInfo, err = tx.GetImageFromAnyProject(ctx, fp)
++ _, otherImg, err = tx.GetImageFromAnyProject(ctx, fp)
+
+ return err
+ })
+ if err == nil {
++ // Only reuse another project's image when the caller may see it,
++ // otherwise download it (proving access) and dedupe on disk.
++ reuse := otherImg.Public || r == nil
++ if !reuse {
++ err = s.Authorizer.CheckPermission(ctx, r, auth.ObjectImage(otherImg.Project, otherImg.Fingerprint), auth.EntitlementCanView)
++ if err == nil {
++ reuse = true
++ } else if !api.StatusErrorCheck(err, http.StatusForbidden) {
++ return nil, false, err
++ }
++
++ err = nil
++ }
++
++ if reuse {
++ imgInfo = otherImg
++ } else if args.Server == "" {
++ // No source to prove access against.
++ return nil, false, api.StatusErrorf(http.StatusNotFound, "Image not found")
++ }
++ }
++
++ if err == nil && imgInfo != nil {
+ var nodeAddress string
+
+ err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error {
+@@ -317,9 +342,9 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope
+ return nil, false, fmt.Errorf("Invalid image fingerprint")
+ }
+
+- // Cleanup any leftover from a past attempt
++ // Download to a temporary name so an existing on-disk copy isn't overwritten.
+ destDir := internalUtil.VarPath("images")
+- destName := filepath.Join(destDir, fp)
++ destName := filepath.Join(destDir, fp+".download")
+
+ failure := true
+ cleanup := func() {
+@@ -576,9 +601,12 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope
+ return nil, false, fmt.Errorf("Invalid image fingerprint")
+ }
+
+- // Check if the image path changed (private images)
++ // Reuse an existing on-disk copy if present, otherwise move ours into place.
+ newDestName := filepath.Join(destDir, fp)
+- if newDestName != destName {
++ if util.PathExists(newDestName) {
++ _ = os.Remove(destName)
++ _ = os.Remove(destName + ".rootfs")
++ } else {
+ err = internalUtil.FileMove(destName, newDestName)
+ if err != nil {
+ return nil, false, err
+diff --git a/internal/server/db/images.go b/internal/server/db/images.go
+index 2a730e596..b3719becf 100644
+--- a/internal/server/db/images.go
++++ b/internal/server/db/images.go
+@@ -436,6 +436,7 @@ func (c *ClusterTx) GetImageFromAnyProject(ctx context.Context, fingerprint stri
+
+ object = images[0]
+
++ image.Project = object.Project
+ image.Fingerprint = object.Fingerprint
+ image.Filename = object.Filename
+ image.Size = object.Size
+--
+2.47.3
diff --git a/debian/patches/series b/debian/patches/series
index 45d2cab03b..f04af2fc4f 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -54,3 +54,5 @@
144-GHSA-m3j6-p3v3-qmjv.patch
145-GHSA-p2v3-6wvc-cv3p.patch
146-incus-7.3-fixes.patch
+147-CVE-2026-81500.patch
+148-CVE-2026-81501.patch
signature.asc
Description: This is a digitally signed message part
--- End Message ---