Skip to main content

Distribution Registry CVE-2026-35172

| EUVDEUVD-2026-19446 HIGH
Improper Access Control (CWE-284)
2026-04-06 https://github.com/distribution/distribution GHSA-f2g3-hh2r-cwgc
7.5
CVSS 3.1 · Vendor: https://github.com/distribution/distribution
Share

Severity by source

Vendor (https://github.com/distribution/distribution) PRIMARY
7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
SUSE
HIGH
qualitative
Red Hat
7.5 HIGH
qualitative

Primary rating from Vendor (https://github.com/distribution/distribution).

CVSS VectorVendor: https://github.com/distribution/distribution

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

6
Analysis Updated
Apr 28, 2026 - 00:14 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Apr 28, 2026 - 00:07 vuln.today
cvss_changed
EUVD ID Assigned
Apr 06, 2026 - 19:00 euvd
EUVD-2026-19446
Analysis Generated
Apr 06, 2026 - 19:00 vuln.today
Patch released
Apr 06, 2026 - 19:00 nvd
Patch available
CVE Published
Apr 06, 2026 - 17:53 nvd
HIGH 7.5

DescriptionCVE.org

summary:

distribution can restore read access in repo a after an explicit delete when storage.cache.blobdescriptor: redis and storage.delete.enabled: true are both enabled. the delete path clears the shared digest descriptor but leaves stale repo-scoped membership behind, so a later Stat or Get from repo b repopulates the shared descriptor and makes the deleted blob readable from repo a again.

Severity

HIGH

justification: this is a repo-local authorization bypass after explicit delete, with concrete confidentiality impact and no requirement for write access after the delete event. CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (7.5). CWE-284.

affected version

  • repository: https://github.com/distribution/distribution
  • commit: ab67ffa0bda3712991194841d0fde727464feeb9
  • affected versions: \<= 3.0.x, \<= 2.8.x when redis blob descriptor cache and delete are both enabled
  • affected file:
  • https://github.com/distribution/distribution/blob/ab67ffa0bda3712991194841d0fde727464feeb9/registry/storage/cache/redis/redis.go#L212-L226
  • related callsites:
  • https://github.com/distribution/distribution/blob/ab67ffa0bda3712991194841d0fde727464feeb9/registry/storage/cache/cachedblobdescriptorstore.go#L66-L76
  • https://github.com/distribution/distribution/blob/ab67ffa0bda3712991194841d0fde727464feeb9/registry/storage/linkedblobstore.go#L218-L224
  • https://github.com/distribution/distribution/blob/ab67ffa0bda3712991194841d0fde727464feeb9/registry/storage/linkedblobstore.go#L396-L403

details

the backend access model is repository-link based: once repo a deletes its blob link, later reads from repo a should continue returning ErrBlobUnknown even if the same digest remains linked in repo b.

the issue is the split invalidation path in the redis cache backend:

  1. linkedBlobStore.Delete calls blobAccessController.Clear during repository delete handling.
  2. cachedBlobStatter.Clear forwards that invalidation into the cache layer.
  3. repositoryScopedRedisBlobDescriptorService.Clear checks that the digest is a member of repo a, but then only calls upstream.Clear.
  4. upstream.Clear deletes the shared digest descriptor and does not remove the digest from the repository membership set for repo a.
  5. when repo b later stats or gets the same digest, the shared descriptor is recreated.
  6. repositoryScopedRedisBlobDescriptorService.Stat for repo a accepts the stale membership and now trusts the repopulated shared descriptor, restoring access in the repository that already deleted its link.

this creates a revocation gap at the repository boundary. the blob is briefly inaccessible from repo a right after delete, which confirms the backend link was removed, and then becomes accessible again only because stale redis membership survived while a peer repository repopulated the shared descriptor.

attack scenario

  1. an operator runs distribution with storage.cache.blobdescriptor: redis and storage.delete.enabled: true.
  2. the same digest exists in both repo a and repo b.
  3. the operator deletes the blob from repo a and expects repository-local access to be revoked.
  4. repo a correctly returns blob unknown immediately after the delete.
  5. an anonymous or unprivileged user requests the same digest from repo b, which still legitimately owns it and repopulates the shared descriptor.
  6. a later request for the digest from repo a succeeds again because stale repo-a membership was never revoked from redis.

PoC

attachment: poc.zip

the attached PoC is a deterministic integration harness using miniredis and the pinned distribution source tree.

steps to reproduce

canonical:

bash
unzip -q -o poc.zip -d poc
cd poc
make canonical

expected output:

text
[CALLSITE_HIT]: repositoryScopedRedisBlobDescriptorService.Clear->upstream.Clear->repositoryScopedRedisBlobDescriptorService.Stat
[PROOF_MARKER]: repo_a_access_restored=true repo_a_delete_miss=true repo_b_peer_warm=true
[IMPACT_MARKER]: repo_a_post_delete_read=true confidentiality_boundary_broken=true

control:

bash
unzip -q -o poc.zip -d poc
cd poc
make control

expected control output:

text
[CALLSITE_HIT]: repositoryScopedRedisBlobDescriptorService.Clear->repositoryScopedRedisBlobDescriptorService.Stat
[NC_MARKER]: repo_a_access_restored=false repo_b_peer_warm=true

expected vs actual

  • expected: after repo a deletes its blob link, later reads from repo a should keep returning blob unknown even if repo b still references the same digest and warms cache state.
  • actual: repo a first returns blob unknown, then repo b repopulates the shared descriptor, and repo a serves the deleted digest again through stale repo-scoped redis membership.

impact

the confirmed impact is repository-local confidentiality failure after explicit delete. an operator can remove sensitive content from repo a, observe revocation working immediately after the delete, and still have the same content become readable from repo a again as soon as repo b refreshes the shared descriptor for that digest.

this is not a claim about global blob deletion. the bounded claim is that repository-local revocation fails, which breaks the expectation that deleting a blob link from one repository prevents further reads from that repository.

remediation

the safest fix is to make redis invalidation revoke repo-scoped state together with the backend link deletion. in practice that means removing the digest from the repository membership set, deleting the repo-scoped descriptor hash, and keeping that cleanup atomic enough that peer-repository warming cannot restore access in the repository that already deleted its link.

poc.zip PR_DESCRIPTION.md attack_scenario.md

AnalysisAI

Distribution container registry versions ≤3.0.x and ≤2.8.x restore read access to explicitly deleted blobs when Redis blob descriptor caching and storage deletion are both enabled. After an administrator deletes a blob from repository A, the deletion briefly succeeds, but when repository B later accesses the same digest, it repopulates the shared Redis descriptor cache. Repository A then regains unauthorized read access to the deleted blob because stale repository-scoped membership metadata was never invalidated from Redis. This authorization bypass defeats repository-local content revocation with concrete confidentiality impact. CVSS 7.5 (HIGH) with network attack vector, low complexity, and no authentication required. EPSS exploitation probability is very low (0.03%, 9th percentile), suggesting limited real-world targeting despite public POC availability. Vendor-released patch confirms the issue and provides a fix in version 3.1.0.

Technical ContextAI

Distribution (formerly Docker Registry) is a stateless, highly scalable container registry implementation. The vulnerability lies in the Redis-backed blob descriptor cache layer (storage.cache.blobdescriptor: redis), which implements a two-tier state model: shared digest descriptors and repository-scoped membership sets. When storage.delete.enabled: true is configured, the delete code path (linkedBlobStore.Delete → blobAccessController.Clear → cachedBlobStatter.Clear → repositoryScopedRedisBlobDescriptorService.Clear) only invalidates the shared digest descriptor via upstream.Clear but fails to remove the digest from the repository-scoped membership set in Redis. Later, when a peer repository (repo B) legitimately accesses the same digest via Stat or Get, it repopulates the shared descriptor. The original repository (repo A) then trusts the stale membership metadata and re-authorizes access to content it had already deleted. This maps to CWE-284 (Improper Access Control) because the cache layer violates the repository-link-based access model's revocation invariant: once a repository deletes its blob link, it should never regain read access to that digest regardless of peer repository state. The affected code paths are in registry/storage/cache/redis/redis.go (L212-226), cachedblobdescriptorstore.go (L66-76), and linkedblobstore.go (L218-224, L396-403).

RemediationAI

Upgrade to Distribution 3.1.0 or later, which includes the vendor-released patch (commit 078b0783f239b4115d1a979e66f08832084e9d1d) that atomically removes digests from repository-scoped membership sets during blob deletion. Refer to GitHub Security Advisory GHSA-f2g3-hh2r-cwgc for upgrade guidance. If immediate upgrade is not feasible, implement these compensating controls with their trade-offs: (1) Disable storage.delete.enabled: true to prevent delete operations entirely-this eliminates the vulnerability but also removes the ability to reclaim storage, impacting compliance workflows that depend on permanent deletion. (2) Switch from storage.cache.blobdescriptor: redis to storage.cache.blobdescriptor: inmemory or disable blob descriptor caching entirely-this closes the attack vector but degrades registry performance, especially for Stat-heavy workloads, as every descriptor lookup hits backend storage. (3) Implement external cache invalidation: after deleting a blob from repository A, manually flush the corresponding Redis keys (both the shared descriptor hash and the repository membership set)-this requires custom scripting, introduces operational complexity, and risks race conditions if not executed atomically with the delete operation. None of these workarounds provide the same security guarantee as the patched code, which ensures repository-local revocation survives peer repository cache warming.

More in Redis

View all
CVE-2026-48172 CRITICAL POC
10.0 May 21

LiteSpeed User-End cPanel Plugin before 2.4.5 allows privilege escalation (possibly to root), as exploited in the wild i

CVE-2025-49844 CRITICAL POC
9.9 Oct 03

UAF in Redis 8.2.1 via crafted Lua scripts by authenticated users. EPSS 12.4%. Patch available.

CVE-2022-0543 CRITICAL POC
10.0 Feb 18

It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific

CVE-2018-11218 CRITICAL POC
9.8 Jun 17

Memory Corruption was discovered in the cmsgpack library in the Lua subsystem in Redis before 3.2.12, 4.x before 4.0.10,

CVE-2025-46817 HIGH POC
7.0 Oct 03

Redis is an open source, in-memory database that persists on disk. Versions 8.2.1 and below allow an authenticated user

CVE-2015-4335 CRITICAL POC
10.0 Jun 09

Redis before 2.8.21 and 3.x before 3.0.2 allows remote attackers to execute arbitrary Lua bytecode via the eval command.

CVE-2016-8339 CRITICAL POC
9.8 Oct 28

A buffer overflow in Redis 3.2.x prior to 3.2.4 causes arbitrary code execution when a crafted command is sent. Rated cr

CVE-2026-27574 CRITICAL POC
9.9 Feb 21

Code injection in OneUptime monitoring via custom JS monitor using vm module. PoC and patch available.

CVE-2021-31649 CRITICAL POC
9.8 Jun 24

In applications using jfinal 4.9.08 and below, there is a deserialization vulnerability when using redis,may be vulnerab

CVE-2020-11981 CRITICAL POC
9.8 Jul 17

An issue was found in Apache Airflow versions 1.10.10 and below. Rated critical severity (CVSS 9.8), this vulnerability

CVE-2018-11219 CRITICAL POC
9.8 Jun 17

An Integer Overflow issue was discovered in the struct library in the Lua subsystem in Redis before 3.2.12, 4.x before 4

CVE-2024-23998 CRITICAL POC
9.6 Jul 05

goanother Another Redis Desktop Manager =<1.6.1 is vulnerable to Cross Site Scripting (XSS) via src/components/Setting.v

Vendor StatusVendor

SUSE

Severity: High
Product Status
SUSE Linux Enterprise High Performance Computing 15 SP7 Fixed
SUSE Linux Enterprise Micro 5.5 Fixed
SUSE Linux Enterprise Module for Containers 15 SP7 Fixed
SUSE Linux Enterprise Module for Package Hub 15 SP7 Fixed
SUSE Linux Enterprise Server 15 SP7 Fixed

Share

CVE-2026-35172 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy