1SEAL-2026-006: ContainerID path traversal in BuildKit gateway frontend can escape runc executor root
summary
a malicious gateway.v0 frontend can choose an attacker-controlled
ContainerID over the frontend gRPC boundary, and BuildKit uses that value
as a filesystem path component for the runc executor bundle directory.
because the executor path is built from ContainerID with join-and-clean
semantics instead of strict identifier validation, values containing ../
segments can escape the intended executor root. BuildKit then performs daemon-side
filesystem operations on the escaped path, including directory creation,
config.json write, and cleanup.
in the end-to-end PoC submitted with the report, the malicious frontend escaped into a
bind-mounted /host directory and caused BuildKit to create
host/owned/config.json. the later runc failure came too late:
the host-visible write had already happened.
the public GHSA scope is narrower than a generic "all frontend use" reading:
exploitation requires an untrusted custom frontend supplied via #syntax or
--build-arg BUILDKIT_SYNTAX. normal use of a well-known frontend image such
as docker/dockerfile is not affected.
published advisory
| field | value |
|---|---|
| public advisory | GHSA-4c29-8rgm-jvjj |
| CVE | CVE-2026-33747 |
| published | 2026-03-25 |
severity
HIGH — CVSS 3.1 Base Score: 8.4
Vector: AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
public advisory assessment: High.
researcher assessment: HIGH by default as a real confinement and filesystem-boundary break.
deployment-sensitive ceiling remains CRITICAL in multi-tenant or untrusted-frontend
deployments where buildkitd has meaningful host-visible filesystem access.
affected versions
all BuildKit versions <= v0.28.0.
confirmed during reporting on commit
bb7bb2048e724af22a14598043964476cb45c2cc.
patched versions
fixed in v0.28.1+.
am i affected?
you are affected if all of the following are true:
- you run BuildKit
<= v0.28.0 - your build flow accepts a custom frontend via
#syntaxor--build-arg BUILDKIT_SYNTAX - that frontend is untrusted, user-controlled, or otherwise outside your normal trusted frontend allowlist
under the public GHSA scope, you are not affected if:
- you only use a well-known frontend image such as
docker/dockerfile - you do not allow untrusted custom frontend selection in your build flow
- you have already upgraded to
v0.28.1+
workarounds
if you cannot upgrade immediately:
- do not accept untrusted custom frontends through
#syntaxorBUILDKIT_SYNTAX - pin frontend images to known-good references instead of allowing arbitrary user-supplied frontend images
- reduce
buildkitdaccess to host-visible paths and avoid unnecessary bind mounts into the daemon environment
root cause
the vulnerable trust-boundary crossing is straightforward:
- a
gateway.v0frontend sendsNewContainerparameters, includingContainerID - BuildKit accepts that identifier across the frontend boundary
(*runcExecutor).Runuses the value as a bundle path component under the executor root- path normalization resolves traversal sequences outside the intended root
- daemon-side filesystem sinks act on the escaped path
the attacker-controlled input source is in frontend/gateway/gateway.go at
the pinned report commit. the filesystem sink is (*runcExecutor).Run in
executor/runcexecutor/executor.go.
this is a confinement failure, not just a frontend-trust debate. even if operators intentionally run custom frontends, the daemon must still preserve its own filesystem boundary. "frontend can request builds" is not equivalent to "frontend can steer daemon file operations outside the executor root."
exploitation chain
BuildKit documents gateway.v0 as a frontend mode that can use an OCI image
implementing the frontend API.
a malicious frontend can therefore participate in the normal frontend protocol while
returning a crafted identifier such as ../../host/owned.
the executor then derives a bundle path outside w.root, creates directories
there, writes OCI runtime state such as config.json, and later attempts
cleanup on the same escaped location. in the submitted PoC, this produced a deterministic
host-visible write before the build failed.
the impact is deployment-dependent:
- in rootful or host-mounted deployments, the out-of-root write/remove can touch meaningful host paths
- in weaker exposure models, the same bug still breaks a core integrity invariant: frontend-controlled data must not escape the executor root and redirect daemon filesystem operations
detection
no public indicator set has been published for this issue, so practical detection is exposure-focused.
review build definitions and CI inputs for untrusted use of #syntax or
BUILDKIT_SYNTAX, especially on BuildKit <= v0.28.0.
if buildkitd had host-visible bind mounts or similar filesystem exposure,
inspect reachable paths for unexpected executor-style directories or
config.json artifacts created during failed or suspicious builds. in the
submitted PoC, the visible artifact was host/owned/config.json.
fix
the public fix shipped in v0.28.1+ and includes commit
099cf80f5ebc935c48d2925499bffe703a54cff4,
executor: validate container IDs centrally.
the published remediation aligns with the core report direction:
- validate
ContainerIDas a strict non-path identifier at the trust boundary - reject path separators and traversal segments instead of relying on path cleaning
- keep filesystem operations confined to the intended executor root
timeline
| date | event |
|---|---|
| 2026-02-10 | reported privately to Docker security |
| 2026-03-25 | public advisory GHSA-4c29-8rgm-jvjj published with CVE-2026-33747 |
| 2026-03-25 | BuildKit v0.28.1 released with the public fix |
credit
discovered and reported by Oleh Konko (1seal).
the public GitHub advisory credits 1seal as reporter.
references
- public advisory: GHSA-4c29-8rgm-jvjj
- public fix release: BuildKit v0.28.1
- public fix commit: 099cf80f5ebc935c48d2925499bffe703a54cff4
- pinned sink callsite
- pinned attacker-controlled input source
- developer docs, frontend model
- buildctl reference, gateway.v0
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory