Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessConfigure registry with Redis cache + deletion enabled
-
DeliveryShare digest across repo A and repo B
-
ExploitAdmin deletes blob from repo A
-
ExecutionAttacker triggers repo B access to repopulate shared descriptor
-
PersistAttacker reads deleted blob from repo A via stale membership
-
ImpactRestored confidentiality breach
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V8 - Authorization Access control enforcement
MITRE ATT&CK
1 technique across 1 tactic: Defense Evasion The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires BOTH non-default configuration settings enabled simultaneously: storage.cache.blobdescriptor: redis (enables Redis-backed blob descriptor caching instead of default in-memory or no caching) AND storage.delete.enabled: true (enables blob deletion API, often disabled in production to prevent accidental data loss). Additionally requires multi-repository blob sharing-the same content-addressable digest must exist in at least two repositories (repo A where deletion occurs and repo B that repopulates cache). Exploitation depends on specific operational timing: an administrator must delete a blob from repo A, then a user (can be unauthenticated per PR:N) must access the same digest from repo B before Redis cache entry expires, followed by a second access to the deleted digest from repo A. Default Distribution deployments using filesystem-backed caching or with blob deletion disabled are not vulnerable. The vulnerability affects only repository-scoped read access, not global blob deletion or other repository isolation boundaries. |
| Impact | CVSS 7.5 (HIGH) reflects maximum confidentiality impact (C:H) with network reachability (AV:N), low complexity (AC:L), and no authentication barrier (PR:N/UI:N). However, real-world risk is conditional and significantly lower than the raw score suggests. EPSS score of 0.03% (9th percentile) indicates minimal observed exploitation interest despite public POC availability since the GitHub advisory disclosure. SSVC framework confirms POC exists and the attack is automatable with partial technical impact, but exploitation status remains at POC-only-no evidence of active targeting. The critical limiting factor: deployment requires BOTH storage.cache.blobdescriptor: redis AND storage.delete.enabled: true, which is not the default Distribution configuration. Most production registries either disable blob deletion entirely or use filesystem-backed descriptor caching. The attack also requires multi-repository blob sharing (same digest in repo A and repo B), a specific operational sequence (delete from A, then access from B), and provides only read access to previously-accessible content-not privilege escalation or arbitrary data exfiltration. The confidentiality impact is real for environments that rely on repository-local deletion for compliance or secret rotation, but the narrow deployment profile explains the low EPSS score. This is a high-CVSS, low-real-world-prevalence issue: prioritize for registries using Redis caching with deletion enabled, but most deployments are not exposed. Verified patch availability in 3.1.0 reduces urgency further. |
| Exploit ScenarioDerived | An attacker identifies a Distribution registry running with Redis blob descriptor caching and deletion enabled. The target registry hosts both a public repository (repo B) and a private repository (repo A) that share a common base layer digest. When an administrator deletes sensitive content from repo A (e.g., a layer containing leaked credentials), the attacker observes the deletion succeed and repo A returns 404 for the blob. The attacker then triggers a pull from repo B, which legitimately contains the same digest. This pull operation causes repo B to repopulate the shared Redis descriptor cache. The attacker immediately requests the previously deleted digest from repo A again and successfully retrieves the content that was just deleted, bypassing repository-local access controls. The attack requires no authentication (PR:N), works over the network (AV:N), and is deterministic (AC:L) given the configuration prerequisites. POC code demonstrating the exact sequence is publicly available in poc.zip. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv2 – Apr 28, 2026
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.
Earlier versions of this analysis (1)
Repository-scoped authorization bypass in distribution container registry allows restored read access to explicitly deleted blobs when Redis caching is enabled. Affects distribution/distribution v2.8.x and v3.0.x when both storage.cache.blobdescriptor: redis and storage.delete.enabled: true are configured. Unauthenticated remote attackers can retrieve sensitive content deleted from repo A after repo B repopulates the shared Redis descriptor cache, exposing confidential data that operators believed was permanently revoked. CVSS 7.5 (High). Publicly available exploit code exists with deterministic PoC demonstrating the state-machine race condition. EPSS data not provided, but the low attack complexity (AC:L) and no privilege requirement (PR:N) indicate straightforward exploitation once the vulnerable configuration is identified.
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.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 3 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
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).
Know when Redis gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Primary rating from Vendor (https://github.com/distribution/distribution).
CVSS VectorVendor: https://github.com/distribution/distribution
Lifecycle Timeline
3DescriptionCVE.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:
linkedBlobStore.DeletecallsblobAccessController.Clearduring repository delete handling.cachedBlobStatter.Clearforwards that invalidation into the cache layer.repositoryScopedRedisBlobDescriptorService.Clearchecks that the digest is a member ofrepo a, but then only callsupstream.Clear.upstream.Cleardeletes the shared digest descriptor and does not remove the digest from the repository membership set forrepo a.- when
repo blater stats or gets the same digest, the shared descriptor is recreated. repositoryScopedRedisBlobDescriptorService.Statforrepo aaccepts 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
- an operator runs distribution with
storage.cache.blobdescriptor: redisandstorage.delete.enabled: true. - the same digest exists in both
repo aandrepo b. - the operator deletes the blob from
repo aand expects repository-local access to be revoked. repo acorrectly returnsblob unknownimmediately after the delete.- an anonymous or unprivileged user requests the same digest from
repo b, which still legitimately owns it and repopulates the shared descriptor. - a later request for the digest from
repo asucceeds 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:
unzip -q -o poc.zip -d poc
cd poc
make canonicalexpected output:
[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=truecontrol:
unzip -q -o poc.zip -d poc
cd poc
make controlexpected control output:
[CALLSITE_HIT]: repositoryScopedRedisBlobDescriptorService.Clear->repositoryScopedRedisBlobDescriptorService.Stat
[NC_MARKER]: repo_a_access_restored=false repo_b_peer_warm=trueexpected vs actual
- expected: after
repo adeletes its blob link, later reads fromrepo ashould keep returningblob unknowneven ifrepo bstill references the same digest and warms cache state. - actual:
repo afirst returnsblob unknown, thenrepo brepopulates the shared descriptor, andrepo aserves 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.
Executive ViewAI
Distribution container registry versions 3.0.x and 2.8.x contain an authorization bypass that allows unauthorized read access to deleted blobs when Redis caching is enabled, enabling attackers to recover sensitive container images or artifacts after deletion without authentication. This vulnerability undermines content revocation controls and exposes organizations to data exposure when repositories share cached blob descriptors, requiring immediate patching to restore repository isolation.
Apply vendor patch. If patching cannot be applied immediately: disable Redis blob descriptor caching (set REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR to 'none' or remove caching configuration); implement network-level access controls to restrict registry access to authorized users only; monitor registry deletion operations and validate deletion succeeded before considering content revoked; segregate repositories with sensitive content to isolated registry instances without shared Redis caches.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.
Privilege escalation to root in the LiteSpeed User-End cPanel Plugin (versions 2.3 through before 2.4.5) lets attackers
UAF in Redis 8.2.1 via crafted Lua scripts by authenticated users. EPSS 12.4%. Patch available.
Authenticated Redis users with permission to run server-side Lua can trigger an integer overflow (CWE-190) in the bundle
It was discovered, that redis, a persistent key-value database, due to a packaging issue, is prone to a (Debian-specific
Memory Corruption was discovered in the cmsgpack library in the Lua subsystem in Redis before 3.2.12, 4.x before 4.0.10,
Redis before 2.8.21 and 3.x before 3.0.2 allows remote attackers to execute arbitrary Lua bytecode via the eval command.
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
Authenticated Lua script execution in Redis 8.2.1 and earlier can be abused with a crafted long-string delimiter to trig
Authenticated remote code execution in OpenC3 COSMOS (Ruby gem 'openc3' versions 5.1.0 through 7.2.1) lets any logged-in
CVE-2025-46818 is a sandbox-escape flaw in Redis's embedded Lua scripting engine (all versions up to and including 8.2.1
Code injection in OneUptime monitoring via custom JS monitor using vm module. PoC and patch available.
RESP protocol command injection in Predis, the popular PHP client for Redis and Valkey, affects versions 3.0.0-RC1 throu
Same weakness CWE-284 – Improper Access Control
View allSame technique Authentication Bypass
View allVendor 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 |
| SUSE Linux Enterprise Server 16.0 | Fixed |
| SUSE Linux Enterprise Server 16.1 | Fixed |
| SUSE Linux Enterprise Server for SAP Applications 15 SP7 | Fixed |
| SUSE Linux Enterprise Server for SAP applications 16.0 | Fixed |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Fixed |
| SUSE Linux Micro 6.0 | Fixed |
| SUSE Linux Micro 6.0 | Fixed |
| SUSE Linux Micro 6.1 | Fixed |
| SUSE Linux Micro 6.1 | Fixed |
| SUSE Linux Micro 6.2 | Fixed |
| SUSE Linux Micro 6.2 | Fixed |
| openSUSE Leap 15.6 | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP4 | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP4-LTSS | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP5 | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP5-LTSS | Fixed |
| SUSE Linux Enterprise Module for Containers 15 SP4 | Fixed |
| SUSE Linux Enterprise Module for Containers 15 SP5 | Fixed |
| SUSE Linux Enterprise Module for Containers 15 SP6 | Fixed |
| SUSE Linux Enterprise Server 15 SP4 | Fixed |
| SUSE Linux Enterprise Server 15 SP4-LTSS | Fixed |
| SUSE Linux Enterprise Server 15 SP5 | Fixed |
| SUSE Linux Enterprise Server 15 SP5-LTSS | Fixed |
| SUSE Linux Enterprise Server 15 SP6 | Fixed |
| SUSE Linux Enterprise Server 15 SP6-LTSS | Fixed |
| SUSE Linux Enterprise Server for SAP Applications 15 SP6 | Fixed |
| SUSE Manager Proxy 4.3 | Fixed |
| SUSE Manager Retail Branch Server 4.3 | Fixed |
| SUSE Manager Server 4.3 | Fixed |
| SUSE Enterprise Storage 7.1 | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP3 | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP3-ESPOS | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP3-LTSS | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP4-ESPOS | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP5-ESPOS | Fixed |
| SUSE Linux Enterprise High Performance Computing 15 SP6 | Fixed |
| SUSE Linux Enterprise Module for Containers 15 SP3 | Fixed |
| SUSE Linux Enterprise Module for Package Hub 15 SP3 | Fixed |
| SUSE Linux Enterprise Module for Package Hub 15 SP4 | Fixed |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Fixed |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Fixed |
| SUSE Linux Enterprise Server 15 SP3 | Fixed |
| SUSE Linux Enterprise Server 15 SP3-BCL | Fixed |
| SUSE Linux Enterprise Server 15 SP3-LTSS | Fixed |
| SUSE Linux Enterprise Server for SAP Applications 15 SP3 | Fixed |
| SUSE Linux Enterprise Server for SAP Applications 15 SP4 | Fixed |
| SUSE Linux Enterprise Server for SAP Applications 15 SP5 | Fixed |
| SUSE Manager Proxy 4.2 | Fixed |
| SUSE Manager Retail Branch Server 4.2 | Fixed |
| SUSE Manager Server 4.2 | Fixed |
| openSUSE Leap 15.3 | Fixed |
| openSUSE Leap 15.4 | Fixed |
| openSUSE Leap 15.5 | Fixed |
| openSUSE Leap Micro 5.5 | Fixed |
| suse/helm | Fixed |
EUVD-2026-19446
GHSA-f2g3-hh2r-cwgc
NVD
CVE.org
EPSS
Vulnrichment
VulDB
Exploit-DB