Incus does not verify combined fingerprint when downloading images from simplestreams servers
Description
Incus is a system container and virtual machine manager. Prior to version 6.23.0, a lack of validation of the image fingerprint when downloading from simplestreams image servers opens the door to image cache poisoning and under very narrow circumstances exposes other tenants to running attacker controlled images rather than the expected one. Version 6.23.0 patches the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/lxc/incus/v6/clientGo | < 6.23.0 | 6.23.0 |
Affected products
1Patches
404e97418189fclient/simplestreams: Validate the full image hash
1 file changed · +46 −0
client/simplestreams_images.go+46 −0 modified@@ -97,6 +97,14 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe httpTransport.ResponseHeaderTimeout = 30 * time.Second httpClient.Transport = httpTransport + // Get the image and expand the fingerprint. + image, err := r.ssClient.GetImage(fingerprint) + if err != nil { + return nil, err + } + + fingerprint = image.Fingerprint + // Get the file list files, err := r.ssClient.GetFiles(fingerprint) if err != nil { @@ -240,6 +248,44 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe } } + // Validate the full image hash. + // + // Normally we'd do that as we download the image to avoid having to + // re-read the data, but because the simplestreams allows retries (HTTP to HTTPS), + // we don't have a clean reader that can be used for that. + // + // Another situation where we couldn't do a streaming hash anyway is when processing delta images. + hash256 := sha256.New() + + if resp.MetaSize > 0 && req.MetaFile != nil { + _, err = req.MetaFile.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + + _, err := io.Copy(hash256, req.MetaFile) + if err != nil { + return nil, err + } + } + + if resp.RootfsSize > 0 && req.RootfsFile != nil { + _, err = req.RootfsFile.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + + _, err := io.Copy(hash256, req.RootfsFile) + if err != nil { + return nil, err + } + } + + hash := fmt.Sprintf("%x", hash256.Sum(nil)) + if hash != fingerprint { + return nil, fmt.Errorf("Image fingerprint doesn't match. Got %s expected %s", hash, fingerprint) + } + return &resp, nil }
72688b7d9400incusd: Update for changes to incus.ImageFileRequest
2 files changed · +4 −4
cmd/incusd/daemon_images.go+2 −2 modified@@ -412,8 +412,8 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope // Download the image var resp *incus.ImageFileResponse request := incus.ImageFileRequest{ - MetaFile: io.WriteSeeker(dest), - RootfsFile: io.WriteSeeker(destRootfs), + MetaFile: io.ReadWriteSeeker(dest), + RootfsFile: io.ReadWriteSeeker(destRootfs), ProgressHandler: progress, Canceler: canceler, DeltaSourceRetriever: func(fingerprint string, file string) string {
cmd/incusd/images.go+2 −2 modified@@ -4555,8 +4555,8 @@ func imageImportFromNode(imagesDir string, client incus.InstanceServer, fingerpr defer func() { _ = rootfsFile.Close() }() getReq := incus.ImageFileRequest{ - MetaFile: io.WriteSeeker(metaFile), - RootfsFile: io.WriteSeeker(rootfsFile), + MetaFile: io.ReadWriteSeeker(metaFile), + RootfsFile: io.ReadWriteSeeker(rootfsFile), } getResp, err := client.GetImageFile(fingerprint, getReq)
4a80447c52d6incus: Update for changes to incus.ImageFileRequest
1 file changed · +2 −2
cmd/incus/image.go+2 −2 modified@@ -576,8 +576,8 @@ func (c *cmdImageExport) Run(cmd *cobra.Command, args []string) error { } req := incus.ImageFileRequest{ - MetaFile: io.WriteSeeker(dest), - RootfsFile: io.WriteSeeker(destRootfs), + MetaFile: io.ReadWriteSeeker(dest), + RootfsFile: io.ReadWriteSeeker(destRootfs), ProgressHandler: progress.UpdateProgress, }
ee26f72524abclient: Make ImageFileRequest require a ReadWriteSeeker
1 file changed · +2 −2
client/interfaces.go+2 −2 modified@@ -502,10 +502,10 @@ type ImageCreateArgs struct { // The ImageFileRequest struct is used for an image download request. type ImageFileRequest struct { // Writer for the metadata file - MetaFile io.WriteSeeker + MetaFile io.ReadWriteSeeker // Writer for the rootfs file - RootfsFile io.WriteSeeker + RootfsFile io.ReadWriteSeeker // Progress handler (called whenever some progress is made) ProgressHandler func(progress ioprogress.ProgressData)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-p8mm-23gg-jc9rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33542ghsaADVISORY
- github.com/lxc/incus/commit/04e97418189f743411884afb81a3384e6218b8cdghsaWEB
- github.com/lxc/incus/commit/4a80447c52d6bc05d3322feeb5395f581e7a80e4ghsaWEB
- github.com/lxc/incus/commit/72688b7d9400c8f3c17ad0f93a7c1aeb89627307ghsaWEB
- github.com/lxc/incus/commit/ee26f72524ab60a4abcfd4e52667c52bb24364fcghsaWEB
- github.com/lxc/incus/releases/tag/v6.23.0ghsaWEB
- github.com/lxc/incus/security/advisories/GHSA-p8mm-23gg-jc9rghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.