CVE-2026-61833
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion
zot is a container image and artifact registry based on the Open Container Initiative Distribution Specification. Prior to 2.1.18, the bearer authentication handler in pkg/api/authn.go maps every HTTP method other than GET and HEAD to the push action, so DELETE requests are not checked for the distinct delete permission. Bearer-authenticated requests also bypass the fine-grained DistSpecAuthzHandler path in pkg/api/authz.go, while DeleteManifest and DeleteBlob perform no independent delete-permission check. A remote attacker with a bearer token limited to pull and push actions can therefore delete manifests and blobs within the token's repository scope, making images unavailable and allowing repository history to be altered despite the token lacking delete authorization. This issue is fixed in version 2.1.18.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.1
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion
zot is a container image and artifact registry based on the Open Container Initiative Distribution Specification. Prior to 2.1.18, the bearer authentication handler in pkg/api/authn.go maps every HTTP method other than GET and HEAD to the push action, so DELETE requests are not checked for the distinct delete permission. Bearer-authenticated requests also bypass the fine-grained DistSpecAuthzHandler path in pkg/api/authz.go, while DeleteManifest and DeleteBlob perform no independent delete-permission check. A remote attacker with a bearer token limited to pull and push actions can therefore delete manifests and blobs within the token's repository scope, making images unavailable and allowing repository history to be altered despite the token lacking delete authorization. This issue is fixed in version 2.1.18.
- Record
CVE-2026-61833- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-285
- Updated
- 2026-11-02
EUVDEUVD-2026-83187
8.1
zot is a container image and artifact registry based on the Open Container Initiative Distribution Specification. Prior to 2.1.18, the bearer authentication handler in pkg/api/authn.go maps every HTTP method other than GET and HEAD to the push action, so DELETE requests are not checked for the distinct delete permission. Bearer-authenticated requests also bypass the fine-grained DistSpecAuthzHandler path in pkg/api/authz.go, while DeleteManifest and DeleteBlob perform no independent delete-permission check. A remote attacker with a bearer token limited to pull and push actions can therefore delete manifests and blobs within the token's repository scope, making images unavailable and allowing repository history to be altered despite the token lacking delete authorization. This issue is fixed in version 2.1.18.
- Record
EUVD-2026-83187- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-qg67-7m6v-qg25
8.1
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion
Summary
A bearer token with only pull and push scopes can successfully delete manifests and blobs from a zot registry. The bearer authentication handler maps all non-GET/HEAD HTTP methods, including DELETE, to the "push" action, and the DistSpecAuthzHandler middleware is bypassed entirely for bearer-authenticated requests. This allows any client holding a push-only bearer token to delete arbitrary manifests and blobs within the token's repository scope, in violation of the Docker Distribution Token Authentication Specification.
Details
The vulnerability exists in two interacting components:
1. Action Mapping Collapse (pkg/api/authn.go:571–586)
The bearer authentication handler maps HTTP methods to token scope actions using a binary check:
action := "pull"
if m := request.Method; m != http.MethodGet && m != http.MethodHead {
action = "push"
}
This collapses DELETE, PUT, PATCH, and POST into a single "push" action. The "delete" action is never assigned.
2. Authorization Bypass for Bearer Auth (pkg/api/authz.go:270–275, 318–323)
When a request is authenticated via bearer token, the DistSpecAuthzHandler middleware, which performs fine-grained action inference (distinguishing create, read, update, and delete) is bypassed entirely:
if err != nil || (authnMwCtx != nil && authnMwCtx.AuthnType == BEARER) {
next.ServeHTTP(response, request)
return
}
3. No Handler-Level Authorization Check
Neither DeleteManifest (routes.go:799–884) nor DeleteBlob (routes.go:1192–1241) performs an independent authorization check for delete permission before executing the deletion.
Deviation from Specification and Reference Implementation
The [Docker Distribution Token Scope Documentation](https://distribution.github.io/distribution/spec/auth/scope/) defines delete as a distinct action separate from push. The reference implementation ([distribution/distribution](https://github.com/distribution/distribution/blob/main/registry/handlers/app.go)) correctly maps DELETE requests to the "delete" action:
case http.MethodDelete:
records = append(records,
auth.Access{
Resource: resource,
Action: "delete",
})
Furthermore, zot's own native access-control configuration explicitly distinguishes delete as a separate permission from create and update, confirming the project's intent that delete is a distinct authorization action.
Suggested Fix
In pkg/api/authn.go, the action mapping should distinguish DELETE:
action := "pull"
switch {
case m == http.MethodGet || m == http.MethodHead:
action = "pull"
case m == http.MethodDelete:
action = "delete"
default:
action = "push"
}
The DistSpecAuthzHandler bypass for bearer-authenticated requests (authz.go:270–275) should also be reconsidered to ensure bearer-authenticated requests receive equivalently granular authorization checks.
PoC
Prerequisites: zot v2.1.15 with bearer authentication enabled, and a token server issuing JWTs with actions: ["pull", "push"] (no "delete").
Steps to Reproduce:
- Configure zot with bearer authentication pointing to a token server
- Obtain a bearer token with scope
repository:poc-test:pull,push(no delete) - Upload a config blob:
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/octet-stream" \
-X POST "http://127.0.0.1:5001/v2/poc-test/blobs/uploads/?digest=sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a" \
-d '{}'
# → 201 Created
- Push a manifest tagged
v1.0(succeeds token has push):
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type:…
- Record
GHSA-qg67-7m6v-qg25- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-285
- Updated
- 2026-11-02
NVD
8.1
zot is a container image and artifact registry based on the Open Container Initiative Distribution Specification. Prior to 2.1.18, the bearer authentication handler in pkg/api/authn.go maps every HTTP method other than GET and HEAD to the push action, so DELETE requests are not checked for the distinct delete permission. Bearer-authenticated requests also bypass the fine-grained DistSpecAuthzHandler path in pkg/api/authz.go, while DeleteManifest and DeleteBlob perform no independent delete-permission check. A remote attacker with a bearer token limited to pull and push actions can therefore delete manifests and blobs within the token's repository scope, making images unavailable and allowing repository history to be altered despite the token lacking delete authorization. This issue is fixed in version 2.1.18.
- Record
CVE-2026-61833- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-285
- Updated
- 2026-11-02
OSV
8.1
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion
zot is a container image and artifact registry based on the Open Container Initiative Distribution Specification. Prior to 2.1.18, the bearer authentication handler in pkg/api/authn.go maps every HTTP method other than GET and HEAD to the push action, so DELETE requests are not checked for the distinct delete permission. Bearer-authenticated requests also bypass the fine-grained DistSpecAuthzHandler path in pkg/api/authz.go, while DeleteManifest and DeleteBlob perform no independent delete-permission check. A remote attacker with a bearer token limited to pull and push actions can therefore delete manifests and blobs within the token's repository scope, making images unavailable and allowing repository history to be altered despite the token lacking delete authorization. This issue is fixed in version 2.1.18.
- Record
CVE-2026-61833- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-285
- Updated
- 2026-11-02
OSVGHSA-qg67-7m6v-qg25
8.1
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion
Summary
A bearer token with only pull and push scopes can successfully delete manifests and blobs from a zot registry. The bearer authentication handler maps all non-GET/HEAD HTTP methods, including DELETE, to the "push" action, and the DistSpecAuthzHandler middleware is bypassed entirely for bearer-authenticated requests. This allows any client holding a push-only bearer token to delete arbitrary manifests and blobs within the token's repository scope, in violation of the Docker Distribution Token Authentication Specification.
Details
The vulnerability exists in two interacting components:
1. Action Mapping Collapse (pkg/api/authn.go:571–586)
The bearer authentication handler maps HTTP methods to token scope actions using a binary check:
action := "pull"
if m := request.Method; m != http.MethodGet && m != http.MethodHead {
action = "push"
}
This collapses DELETE, PUT, PATCH, and POST into a single "push" action. The "delete" action is never assigned.
2. Authorization Bypass for Bearer Auth (pkg/api/authz.go:270–275, 318–323)
When a request is authenticated via bearer token, the DistSpecAuthzHandler middleware, which performs fine-grained action inference (distinguishing create, read, update, and delete) is bypassed entirely:
if err != nil || (authnMwCtx != nil && authnMwCtx.AuthnType == BEARER) {
next.ServeHTTP(response, request)
return
}
3. No Handler-Level Authorization Check
Neither DeleteManifest (routes.go:799–884) nor DeleteBlob (routes.go:1192–1241) performs an independent authorization check for delete permission before executing the deletion.
Deviation from Specification and Reference Implementation
The [Docker Distribution Token Scope Documentation](https://distribution.github.io/distribution/spec/auth/scope/) defines delete as a distinct action separate from push. The reference implementation ([distribution/distribution](https://github.com/distribution/distribution/blob/main/registry/handlers/app.go)) correctly maps DELETE requests to the "delete" action:
case http.MethodDelete:
records = append(records,
auth.Access{
Resource: resource,
Action: "delete",
})
Furthermore, zot's own native access-control configuration explicitly distinguishes delete as a separate permission from create and update, confirming the project's intent that delete is a distinct authorization action.
Suggested Fix
In pkg/api/authn.go, the action mapping should distinguish DELETE:
action := "pull"
switch {
case m == http.MethodGet || m == http.MethodHead:
action = "pull"
case m == http.MethodDelete:
action = "delete"
default:
action = "push"
}
The DistSpecAuthzHandler bypass for bearer-authenticated requests (authz.go:270–275) should also be reconsidered to ensure bearer-authenticated requests receive equivalently granular authorization checks.
PoC
Prerequisites: zot v2.1.15 with bearer authentication enabled, and a token server issuing JWTs with actions: ["pull", "push"] (no "delete").
Steps to Reproduce:
- Configure zot with bearer authentication pointing to a token server
- Obtain a bearer token with scope
repository:poc-test:pull,push(no delete) - Upload a config blob:
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/octet-stream" \
-X POST "http://127.0.0.1:5001/v2/poc-test/blobs/uploads/?digest=sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a" \
-d '{}'
# → 201 Created
- Push a manifest tagged
v1.0(succeeds token has push):
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type:…
- Record
GHSA-qg67-7m6v-qg25- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-285
- Updated
- 2026-11-02
OSVGO-2026-6527 No score
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion in zotregistry.dev/zot
zot: Bearer authentication maps DELETE to push scope, allowing unauthorized deletion in zotregistry.dev/zot
- Record
GO-2026-6527- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
zotregistry.dev/zot/v2Go |
before 2.1.18 | 2.1.18 |
zotregistry.dev/zotGo |
All versions | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-61833 is a high vulnerability (CVSS 8.1) in Project-zot Zot, published September 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.81%, the 57th percentile. Fixed in zotregistry.dev/zot/v2 2.1.18.
References
github.com/project-zot/zot/security/advisories/GHSA-qg67-7m6v-qg25
github.com/project-zot/zot/pull/4161
github.com/project-zot/zot/commit/7bb211bcd4352b90f3e99752607fbd1f050bf7ca
github.com/project-zot/zot/releases/tag/v2.1.18
github.com/advisories/GHSA-qg67-7m6v-qg25
github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61833.json
nvd.nist.gov/vuln/detail/CVE-2026-61833
github.com/project-zot/zot
Left out here: the rest of 2 source descriptions. The JSON record has them.