Every CVE Search/ Get API key

CVE-2026-85747

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

6.8 MediumFix available

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
None

Exploit likelihood

0.22%Unchanged since Oct 14, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.8

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
CVE-2026-85747
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Weakness
CWE-354
Updated
2026-11-02
EUVDEUVD-2026-99225 6.8

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
EUVD-2026-99225
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-685w-q87j-wqw3 6.8

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

Summary

registry/client.(*manifests).Get(ctx, dgst, …) fetches a manifest by digest but never hashes the returned body and compares it against the requested digest. A malicious or compromised registry, or any MITM on the registry connection, can serve arbitrary manifest bytes in response to a digest-pinned request and the client returns the substituted content transparently to the caller. Any application relying on digest pinning for supply-chain integrity — image policy controllers, mirrors, pull-through caches — is silently undermined. The bundled pull-through proxy (registry/proxy/proxymanifeststore.go) is itself an unguarded consumer and is directly exploitable.

Details

Root cause: registry/client/repository.go:488-505 reads the response body and passes it directly to distribution.UnmarshalManifest without ever computing digest.FromBytes(body) and comparing against the caller-supplied dgst. The Docker-Content-Digest response header is parsed (lines 251-292 in descriptorFromResponse) only to populate an out-parameter (ReturnContentDigest) and is itself server-supplied — it is never recomputed from the body, so a lying server can set it to whatever value the caller requested while delivering different bytes.

Attack path: An attacker who controls the registry endpoint (malicious registry, compromised upstream, DNS hijack, or TLS MITM) responds to GET /v2/{name}/manifests/{digest} with:

  1. Docker-Content-Digest: <requested-digest> (forged header)
  2. A body containing a different, attacker-chosen manifest

The client calls distribution.UnmarshalManifest on the substituted body, returns the attacker's manifest as if it were the pinned content, and the caller has no way to detect the substitution.

Why it matters: The entire value of digest pinning is to guarantee content-addressable integrity against exactly this threat model (hostile or MITM server). The registry/proxy/proxymanifeststore.go:42-87 pull-through cache calls remoteManifests.Get(ctx, dgst) and immediately writes the result to local storage with no re-verification — so a poisoned remote can permanently inject a bad manifest into the mirror cache, affecting all subsequent consumers.

Relevant code path (repository.go, simplified):

// line ~496
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
    ...
    resp, err := ms.client.Get(u)
    ...
    body, err := ioutil.ReadAll(resp.Body)  // no size limit, no digest check
    ...
    m, _, err := distribution.UnmarshalManifest(ct, body)  // returns attacker bytes
    return m, err
}

PoC

go.mod:

module poc

go 1.24.10

require (
        github.com/distribution/reference v0.6.0
        github.com/docker/distribution v2.8.3+incompatible
        github.com/opencontainers/go-digest v1.0.0
)

require (
        github.com/beorn7/perks v1.0.1 // indirect
        github.com/docker/go-metrics v0.0.1 // indirect
        github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
        github.com/golang/protobuf v1.3.2 // indirect
        github.com/gorilla/mux v1.8.1 // indirect
        github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
        github.com/opencontainers/image-spec v1.1.1 // indirect
        github.com/prometheus/client_golang v1.1.0 // indirect
        github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
        github.com/prometheus/common v0.6.0 // indirect
        github.com/prometheus/procfs v0.0.3 // indirect
        github.com/sirupsen/logrus v1.9.4 // indirect
        golang.org/x/sys v0.13.0 // indirect
)

main.go:

// PoC: registry/client manifests.Get() does not verify the requested digest
// against the returned body. A malicious registry (or MITM) can serve
// arbitrary content for a digest-pinned manifest reference and the client
// will accept it.…
Record
GHSA-685w-q87j-wqw3
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Weakness
CWE-354
Updated
2026-11-02
NVD 6.8

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
CVE-2026-85747
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Weakness
CWE-354
Updated
2026-11-02
OSV 6.8

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
CVE-2026-85747
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Weakness
CWE-354
Updated
2026-11-06
OSVDEBIAN-CVE-2026-85747 6.8

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
DEBIAN-CVE-2026-85747
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Updated
2026-11-02
Show 4 moreShow less
OSVGHSA-685w-q87j-wqw3 6.8

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

Summary

registry/client.(*manifests).Get(ctx, dgst, …) fetches a manifest by digest but never hashes the returned body and compares it against the requested digest. A malicious or compromised registry, or any MITM on the registry connection, can serve arbitrary manifest bytes in response to a digest-pinned request and the client returns the substituted content transparently to the caller. Any application relying on digest pinning for supply-chain integrity — image policy controllers, mirrors, pull-through caches — is silently undermined. The bundled pull-through proxy (registry/proxy/proxymanifeststore.go) is itself an unguarded consumer and is directly exploitable.

Details

Root cause: registry/client/repository.go:488-505 reads the response body and passes it directly to distribution.UnmarshalManifest without ever computing digest.FromBytes(body) and comparing against the caller-supplied dgst. The Docker-Content-Digest response header is parsed (lines 251-292 in descriptorFromResponse) only to populate an out-parameter (ReturnContentDigest) and is itself server-supplied — it is never recomputed from the body, so a lying server can set it to whatever value the caller requested while delivering different bytes.

Attack path: An attacker who controls the registry endpoint (malicious registry, compromised upstream, DNS hijack, or TLS MITM) responds to GET /v2/{name}/manifests/{digest} with:

  1. Docker-Content-Digest: <requested-digest> (forged header)
  2. A body containing a different, attacker-chosen manifest

The client calls distribution.UnmarshalManifest on the substituted body, returns the attacker's manifest as if it were the pinned content, and the caller has no way to detect the substitution.

Why it matters: The entire value of digest pinning is to guarantee content-addressable integrity against exactly this threat model (hostile or MITM server). The registry/proxy/proxymanifeststore.go:42-87 pull-through cache calls remoteManifests.Get(ctx, dgst) and immediately writes the result to local storage with no re-verification — so a poisoned remote can permanently inject a bad manifest into the mirror cache, affecting all subsequent consumers.

Relevant code path (repository.go, simplified):

// line ~496
func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
    ...
    resp, err := ms.client.Get(u)
    ...
    body, err := ioutil.ReadAll(resp.Body)  // no size limit, no digest check
    ...
    m, _, err := distribution.UnmarshalManifest(ct, body)  // returns attacker bytes
    return m, err
}

PoC

go.mod:

module poc

go 1.24.10

require (
        github.com/distribution/reference v0.6.0
        github.com/docker/distribution v2.8.3+incompatible
        github.com/opencontainers/go-digest v1.0.0
)

require (
        github.com/beorn7/perks v1.0.1 // indirect
        github.com/docker/go-metrics v0.0.1 // indirect
        github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
        github.com/golang/protobuf v1.3.2 // indirect
        github.com/gorilla/mux v1.8.1 // indirect
        github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
        github.com/opencontainers/image-spec v1.1.1 // indirect
        github.com/prometheus/client_golang v1.1.0 // indirect
        github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
        github.com/prometheus/common v0.6.0 // indirect
        github.com/prometheus/procfs v0.0.3 // indirect
        github.com/sirupsen/logrus v1.9.4 // indirect
        golang.org/x/sys v0.13.0 // indirect
)

main.go:

// PoC: registry/client manifests.Get() does not verify the requested digest
// against the returned body. A malicious registry (or MITM) can serve
// arbitrary content for a digest-pinned manifest reference and the client
// will accept it.…
Record
GHSA-685w-q87j-wqw3
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Weakness
CWE-354
Updated
2026-11-06
OSVUBUNTU-CVE-2026-85747 6.8

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
UBUNTU-CVE-2026-85747
CVSS 3.1
6.8 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:N/I:H/A:N
Updated
2026-11-02
OSVBIT-distribution-2026-85747 No score

Distribution: Client `manifests.Get()` does not verify returned content against requested digest

Distribution is a toolkit to pack, ship, store, and deliver container content. Prior to 3.1.2, registry/client.(*manifests).Get() reads a manifest returned for a digest-pinned request and passes it to distribution.UnmarshalManifest without hashing the body against the requested digest, allowing a malicious or intercepted registry response to substitute attacker-chosen manifest content even when Docker-Content-Digest claims the requested value. The bundled pull-through proxy in registry/proxy/proxymanifeststore.go can cache and re-serve the substituted manifest. This issue is fixed in version 3.1.2.

Record
BIT-distribution-2026-85747
Updated
2026-11-06
OSVGO-2026-6778 No score

Distribution: Client does not verify returned content against requested digest in github.com/distribution/distribution

Distribution: Client does not verify returned content against requested digest in github.com/distribution/distribution

Record
GO-2026-6778
Updated
2026-11-06

Affected products

Affected packages

Affected packages
PackageAffectedFixed
github.com/docker/distribution
Go
through 2.8.3 None known
github.com/distribution/distribution/v3
Go
before 3.1.2
3.1.2
distribution
Bitnami
before 3.1.2
3.1.2
docker-registry
Debian:12
All versions, plus listed versions
2.8.2+ds1-12.8.3+ds1-12.8.3+ds1-22.8.3+ds1-3
None known
docker-registry
Debian:13
All versions, plus listed versions
2.8.3+ds1-22.8.3+ds1-3
None known
docker-registry
Debian:14
All versions, plus listed versions
2.8.3+ds1-22.8.3+ds1-3
None known
github.com/distribution/distribution
Go
All versions None known
github.com/docker/distribution
Go
All versions None known
docker-registry
Ubuntu:Pro:16.04:LTS
All versions, plus listed versions
2.1.1~ds1-42.1.1~ds1-4ubuntu12.3.0~ds1-12.3.0~ds1-1ubuntu0.1~esm1
None known
docker-registry
Ubuntu:Pro:18.04:LTS
All versions, plus listed versions
2.6.2~ds1-12.6.2~ds1-1ubuntu0.1~esm1
None known
Show 5 moreShow less
Affected packages
PackageAffectedFixed
docker-registry
Ubuntu:20.04:LTS
All versions, plus listed versions
2.6.2~ds1-22.7.1~ds1-22.7.1~ds1-32.7.1~ds1-42.7.1+ds2-5
Show 4 moreShow less
2.7.1+ds2-72.7.1+ds2-7ubuntu0.12.7.1+ds2-7ubuntu0.22.7.1+ds2-7ubuntu0.3
None known
docker-registry
Ubuntu:Pro:22.04:LTS
All versions, plus listed versions
2.7.1+ds2-7build22.8.0+ds1-42.8.0+ds1-4ubuntu0.1~esm12.8.0+ds1-4ubuntu0.1~esm22.8.0+ds1-4ubuntu0.1~esm3
None known
docker-registry
Ubuntu:24.04:LTS
All versions, plus listed versions
2.8.2+ds1-1build12.8.2+ds1-1ubuntu0.24.04.12.8.2+ds1-1ubuntu0.24.04.22.8.2+ds1-1ubuntu0.24.04.3
None known
docker-registry
Ubuntu:26.10
All versions, plus listed versions
2.8.3+ds1-2build12.8.3+ds1-3
None known
docker-registry
Ubuntu:Pro:26.04:LTS
All versions, plus listed versions
2.8.3+ds1-22.8.3+ds1-2build12.8.3+ds1-2ubuntu0.26.04.1~esm1
None known

History

History
RecordedChangedSources
affected, aliases, facts, referencesOSV
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-85747 is a medium vulnerability (CVSS 6.8) in Distribution, published October 13, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.22%, the 12th percentile. Fixed in github.com/distribution/distribution/v3 3.1.2.

References

Show 5 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open