Safeguard
Vulnerability Analysis•

Docker Engine 29.8.2 and BuildKit 0.33.1: 13 CVEs, One DNS Trick

Docker Engine 29.8.2 fixes a DNS trick that turns off registry TLS, a Swarm overlay injection bug and ten BuildKit CVEs, including build cache poisoning.

Safeguard Security Team
7 min read

On 30 September 2026 Docker released Engine 29.8.2 and BuildKit 0.33.1, and the advisories went public the next day. Engine 29.8.2 fixes three Engine CVEs and pulls in BuildKit 0.33.1, which fixes ten more. Two of them deserve attention first: a DNS answer that quietly turns off TLS verification for a registry (CVE-2026-92543), and a malicious image that can poison a shared BuildKit cache so a later, trusted build runs attacker code (CVE-2026-93318).

What's affected

Docker Engine before 29.8.2. The fixed release is 29.8.2.

CVESeverityIssue
CVE-2026-92543HighRegistry hostname resolving to one loopback and one external IP disables certificate verification and allows HTTP fallback
CVE-2026-92542ModerateUnprivileged user on a Linux Swarm node can inject frames into encrypted overlay networks on other nodes
CVE-2026-53493ModerateCrafted OCI image index causes unbounded CPU and memory use on pull (a containerd advisory)

BuildKit v0.33.0 and earlier, fixed in v0.33.1. Engine 29.8.2 bundles it. Standalone buildkitd, CI builders and remote docker buildx builders need their own upgrade.

CVESeverityIssue
CVE-2026-93318HighImage advertises another image's layer DiffIDs, poisoning shared cache
CVE-2026-93317ModerateUnverified container blobs cached through the low-level LLB API (containerd image store)
CVE-2026-93326ModerateCrafted Git source bypasses source policy rules that match on repository URL
CVE-2026-93316HighDaemon panic when a build requests CDI devices and CDI support is disabled
CVE-2026-93315ModerateBuild step can redirect or block proxy CA cleanup (0.31.0 and later)
CVE-2026-93319 to CVE-2026-93323ModerateDaemon panics from malformed frontends and LLB operations, special-file handling, memory exhaustion

How the DNS trick works. Docker treats 127.0.0.0/8 and ::1/128 as insecure registry ranges by default. When checking a registry hostname, the Engine resolved all of its addresses and marked the registry insecure if any single address fell in an insecure range. The connection itself then re-dialed the hostname, not that address. So a DNS answer containing a loopback IP plus an attacker's IP turned off certificate checks and allowed plain HTTP, and the client could end up talking to the attacker. The advisory names the impact: registry credentials sent in X-Registry-Auth disclosed to an attacker host, or a trusted tag replaced with attacker layers. Removing entries from insecure-registries doesn't help, because the loopback ranges are built in.

How the cache poisoning works. BuildKit used the DiffIDs an image advertised to derive cache and snapshot identity, without checking they matched the layer contents. If a daemon with shared or persistent cache processes a malicious image first, a later build of a trusted image can mount the attacker's layer as its base. The advisory's example is replacing /bin/sh, after which the attacker's code can read build secrets and alter output artifacts. Both regular and stargz snapshotters are affected.

How to check if you're exposed

Engine version on each host:

docker version --format '{{.Server.Version}}'

BuildKit version on every builder you use, including remote ones:

docker buildx ls
docker buildx inspect --bootstrap mybuilder | grep -i "buildkit"
buildkitd --version   # standalone daemons

Check that each registry hostname resolves only to the addresses you expect, with no loopback entry:

getent ahosts registry.example.com | awk '{print $1}' | sort -u

For the Swarm issue, see whether the node is in a swarm and runs encrypted overlay networks:

docker info --format '{{.Swarm.LocalNodeState}}'
docker network ls --filter driver=overlay -q | \
  xargs -r docker network inspect --format '{{.Name}} encrypted={{index .Options "encrypted"}}'

What to do now

  1. Upgrade Docker Engine to 29.8.2 on hosts, CI runners and build VMs. For Docker Desktop, check the bundled Engine version in its release notes before assuming you're covered.
  2. Upgrade every BuildKit you run to v0.33.1, not just the one inside Docker Engine. Recreate docker-container and Kubernetes buildx builders so they pull the new image.
  3. Separate trusted and untrusted builds. The CVE-2026-93318 advisory's workaround is to stop sharing BuildKit cache between them: use isolated or ephemeral builders for untrusted image sources, or prune cache after processing them. Disabling stargz alone is not enough.
  4. Prune shared caches after upgrading if untrusted images ever went through the same builder: docker buildx prune -a -f on each builder.
  5. Pin registry resolution until patched. The advisory suggests trusted DNS or a hosts-file entry for the registry hostname, and restricting the daemon's outbound traffic to known registry endpoints. Referencing images by digest helps against substitution but does not protect credentials.
  6. On Swarm nodes you can't patch yet, the advisory gives an iptables rule that stops user processes sending UDP to the data-path port:
iptables -I OUTPUT -p udp \
  --dport "$(docker info --format '{{.Swarm.Cluster.DataPathPort}}')" \
  -m owner --socket-exists -j DROP
  1. If you use BuildKit source policies on Git URLs, add stricter rules on commit SHA or signatures. The advisory notes those checks still applied correctly. Only URL-based rules were bypassable.

Why it matters

Build infrastructure is shared by design. One builder serves many repositories, and its cache is what makes it fast. CVE-2026-93318 turns that sharing into a path from "someone built a malicious image here" to "your production image contains their binary". Treat build caches like any other multi-tenant store and split them by trust level.

The DNS bug is a good example of a check and its use disagreeing. The decision was made on one address and the connection used another. Wherever a security decision depends on name resolution, the code that decides and the code that connects need to use the same answer.

Try it yourself

List every BuildKit builder your CI uses and write its version and cache backend next to it. Mark any builder that builds both third-party images and your release images from the same cache. Split that builder in two, one for untrusted sources and one for release builds, and confirm the release builder's cache starts empty.

Click through it at your own pace, or open it full screen.

Quick check

Does removing a registry from insecure-registries protect against CVE-2026-92543?

No. The advisory explains that Docker Engine always treats 127.0.0.0/8 and ::1/128 as insecure, so a hostname that also resolves to a loopback address still triggers the fallback.

Does upgrading Docker Engine fix a standalone buildkitd?

No. Engine 29.8.2 bundles BuildKit 0.33.1 for its own builder. Standalone daemons, docker-container builders and Kubernetes builders run their own BuildKit and need upgrading separately.

What can an attacker do with a poisoned BuildKit cache?

Per the advisory, a later build using the victim image may mount the attacker's layer as its base, so attacker code runs inside that build. It can read mounted build secrets, alter outputs or hang the build.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.