Mar 1, 2026·5 min read·64 visits
CVE-2024-39897 allows authenticated attackers to read private container layers by abusing the OCI 'mount' API. Zot's deduplication logic failed to verify source permissions, allowing cross-repository mounting of blobs via their digest. Fixed in version 2.1.0.
Zot, an OCI-compliant container image registry, contains an authorization bypass vulnerability in its storage deduplication logic. The issue allows authenticated users to mount (copy) blobs from repositories they do not have permission to access, provided they know the SHA256 digest of the target blob. This effectively grants unauthorized read access to private container layers.
CVE-2024-39897 is a logical access control flaw within the Zot container registry, specifically affecting the implementation of the OCI distribution specification's cross-repository blob mounting feature. In standard OCI operations, a client pushing an image can request to 'mount' a layer (blob) from another repository to avoid re-uploading duplicate data. This optimization relies on the registry verifying that the client has read access to the source repository before allowing the mount.
In affected versions of Zot (prior to 2.1.0), this access check was missing or insufficient. The system performed a global lookup for the requested blob digest in its deduplication cache. If the blob existed anywhere in the storage backend, Zot permitted the mount operation into the attacker's repository. This vulnerability is classified as an Authorization Bypass Through User-Controlled Key (CWE-639), as the blob digest serves as the key to access data without proper authorization context.
The root cause lies in the dissociation between storage deduplication logic and access control enforcement in pkg/api/routes.go. Zot utilizes a global cache (often backed by BoltDB or DynamoDB) to track blobs across all repositories to save space. When a POST request is made to /v2/<repo>/blobs/uploads/?mount=<digest>, the handler invokes imgStore.CheckBlob(name, digest).
In the vulnerable code path, CheckBlob verified the existence of the digest in the global cache. Upon a cache hit, the system assumed the blob was available for mounting and proceeded to link it to the destination repository. The critical failure was the absence of an intervening check to validate the user's permissions against the source repository (or any repository strictly associated with that blob). The system implicitly trusted that knowledge of the digest equated to authorization to access the content, violating the principle of least privilege.
The vulnerability manifests in the handling of the mount query parameter during blob uploads. Below is a conceptual reconstruction of the flaw based on the patch analysis.
Vulnerable Logic (Simplified):
In the CreateBlobUpload handler, the code checks if the blob exists globally:
// BEFORE FIX: Global lookup without permission check
if mountDigest != "" {
// Checks global storage for digest existence
exists, err := imgStore.CheckBlob(mountDigest)
if err == nil && exists {
// Vulnerability: Directly mounts the blob to the user's repo
// No verification that user has Read access to the origin of the blob
err = imgStore.MountBlob(destinationRepo, mountDigest)
return 201, "Created"
}
}Patched Logic: The fix introduces a mandatory authorization step. The system must now identify a repository where the user does have read access that also contains the requested blob. If no such repository is found, the mount is denied, forcing the user to upload the data themselves.
// AFTER FIX: explicit permission validation
if mountDigest != "" {
// 1. Verify existence
exists, _ := imgStore.CheckBlob(mountDigest)
// 2. NEW: Verify authorization via helper
// 'canMount' checks if the user has READ access to at least
// one repository that actually holds this blob.
if exists && c.canMount(user, mountDigest) {
imgStore.MountBlob(destinationRepo, mountDigest)
return 201, "Created"
}
// If check fails, fall back to standard upload (202 Accepted)
return 202, "Accepted"
}Exploitation requires an authenticated account and knowledge of a target blob's SHA256 digest. The attacker does not need to guess the digest; they might obtain it from leaked manifests, public metadata, or side-channel information.
Attack Scenario:
secret-project/app:latest and obtains the digest of a configuration layer (sha256:deadbeef...).attacker/repo requesting to mount the target digest.
POST /v2/attacker/repo/blobs/uploads/?mount=sha256:deadbeef...&from=secret-project/app HTTP/1.1
Host: zot-registry.local
Authorization: Bearer <Attacker_Token>sha256:deadbeef... in its global storage. Ignoring the ACLs on secret-project/app, it links the blob to attacker/repo.GET /v2/attacker/repo/blobs/sha256:deadbeef... request to download the layer content.The primary impact is Confidentiality Loss. The vulnerability allows the leakage of proprietary code, configuration files, binary artifacts, and potential secrets (API keys, certificates) embedded within container layers.
Risk Factors:
The vulnerability is addressed in Zot version 2.1.0. Administrators should upgrade immediately to ensure canMount checks are enforced.
Immediate Mitigation (Configuration): If an upgrade is not feasible, administrators can disable storage deduplication. This prevents the code path that performs the insecure global lookup, though it will increase storage consumption.
{
"storage": {
"dedupe": false
}
}Verification:
After patching, verify the fix using the TestAuthorizationMountBlob logic: attempt to mount a blob from a repository the test user cannot access. The server should respond with 202 Accepted (indicating the mount failed and a fresh upload is expected) rather than 201 Created.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
zot project-zot | < 2.1.0 | 2.1.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 |
| Attack Vector | Network |
| CVSS Score | 4.3 (Medium) |
| EPSS Score | 0.36% |
| Impact | Confidentiality Loss |
| Exploit Status | PoC Available |
CVE-2026-116687 is a Cross-Site Scripting (XSS) vulnerability in the @ckeditor/ckeditor5-engine package of CKEditor 5. When the General HTML Support (GHS) plugin is configured to permit embedded container elements such as <object>, <embed>, or <iframe>, insufficient validation of data: URIs and incomplete stripping of ASCII C0 control characters allow attackers to execute arbitrary JavaScript within an isolated null-origin browser context.
CVE-2026-116686 is a Server-Side Request Forgery (SSRF) and Denial of Service (DoS) vulnerability in the official Model Context Protocol (MCP) Python SDK (mcp package on PyPI). When an MCP client validates structured tool execution results against a server-provided outputSchema, the SDK invokes the Python jsonschema library with its default URI resolution configuration. An untrusted or compromised MCP server can supply an outputSchema containing external $ref directives (using http://, https://, or file:// schemes), forcing the client process to synchronously fetch internal network resources, read local files, or block the main event loop indefinitely.
CVE-2026-116685 (GHSA-5h93-6whr-6q8j) is a sensitive information disclosure vulnerability in the official Model Context Protocol (MCP) Python SDK (`mcp` on PyPI). In versions 1.8.0 through 1.29.1 and 2.0.0 through 2.1.1, the SDK's HTTP-based client transports—including Streamable HTTP, Server-Sent Events (SSE), and OAuth client flows—unconditionally enable automatic HTTP redirect following (`follow_redirects=True`) across origin boundaries. When a configured endpoint returns a redirect to an external origin, the HTTP client forwards caller-supplied custom authentication headers (such as `X-API-Key`) and, in the case of HTTP 307 or 308 responses, replays the full POST request body containing JSON-RPC payloads or OAuth parameters to the untrusted destination.
CVE-2026-116694 (GHSA-f5m9-jfrj-jjj7) is an unbounded resource allocation vulnerability (CWE-770) in the optional Jaeger propagator component (opentelemetry-propagator-jaeger) of the OpenTelemetry Python SDK prior to version 1.45.0. When extracting distributed tracing context from inbound carriers, JaegerPropagator.extract() processes all headers prefixed with 'uberctx-' without enforcing limits on entry count, per-entry byte size, or aggregate byte size. In environments where transport-level header constraints are absent, relaxed, or bypassed (such as non-HTTP message brokers, RPC frameworks, or web servers with elevated buffer configurations), an unauthenticated remote attacker can supply high-volume or oversized baggage headers to trigger excessive URL unquoting, short-lived immutable dictionary allocations, and CPU starvation.
CVE-2026-116684 is an incorrect authorization vulnerability (CWE-863) in the Model Context Protocol (MCP) Python SDK (`modelcontextprotocol/python-sdk`). The server-side bearer authentication middleware (`BearerAuthBackend`) verifies token cryptographic signatures and expiration timestamps via `TokenVerifier`, but fails to verify that the token's audience (`aud` claim or RFC 8707 resource indicator) matches the receiving MCP server's configured `resource_server_url`. In multi-service environments sharing a single OAuth 2.0 Authorization Server, an attacker possessing a valid token for an unrelated service can replay it against the MCP server to gain unauthorized access to protected tools, resources, and prompts.
CVE-2026-116675 is a stored Cross-Site Scripting (XSS) vulnerability in the TinyMCE rich text editor affecting versions 7.9.3 and 8.5.1 through 8.9.2. The flaw resides in the media plugin's serialization logic, where placeholder elements configured with the data-mce-object attribute are converted into real DOM elements using the active window's document context prior to sanitization. When an attacker supplies a media element with an invalid source attribute alongside an inline event handler attribute such as data-mce-p-onerror, the browser resolves the resource and executes the malicious JavaScript before the sanitizer can strip the dangerous attributes.