containerd shim API

Each container managed by containerd has a shim process that exposes a control API over an abstract-namespace Unix socket. A flaw left that socket reachable and unauthenticated from within containers sharing the host network namespace, so a process could call the shim API to create and start new processes under the shim, executing as root on the host.

2 phút đọccập nhật 7 thg 10, 2026florianamette1 người đóng góp
Hỏi trợ lý của bạn (MCP)Sửa trên GitHub

containerd runs a shim process per container (containerd-shim-runc-v2) that owns the container's lifecycle and exposes a ttrpc control API. That API was reachable over a Unix domain socket in Linux's abstract namespace, which, unlike a filesystem socket, is scoped to the network namespace rather than a path. A container that shared the host network namespace could therefore connect to the shim's abstract socket, and because the API performed no caller authentication, it could invoke shim operations such as Exec and Start to create a new process under the shim. Processes created that way run with the shim's privileges as root on the host, giving a container escape without touching the kernel or the image.

Confirm the preconditions:

bash
containerd --version 2>/dev/null            # affected 1.x line before the fix
ls -l /proc/self/ns/net; readlink /proc/1/ns/net   # shared host net namespace needed
cat /proc/net/unix | grep -a containerd-shim        # abstract shim sockets (names begin with @)

An abstract socket name appears in /proc/net/unix with a leading @; a containerd-shim entry there, combined with a shared host network namespace, indicates reachability.

Technique#

bash
# 1. Identify the shim's abstract socket address from /proc/net/unix
#    (abstract names start with a NUL byte, shown as @ or a leading blank)
grep -a shim /proc/net/unix

# 2. Connect to that abstract address and speak the ttrpc shim protocol.
#    Using containerd's own client against the discovered address, call the
#    Task service to create and start a process under the shim, for example:
#    Exec(ExecProcessRequest{ ExecID, Spec{ args:["/bin/sh","-c","id > /tmp/o"] }})
#    Start(StartRequest{ ID, ExecID })

Because the shim runs as root on the host and the new process is a child of the shim outside the original container's confinement, the command executes with host root privileges.

Exploitation notes#

  • The two gating conditions are a shared host network namespace (so the abstract socket is reachable) and the unauthenticated shim API; removing either closes the route. A container with its own network namespace cannot see the host abstract socket.
  • The protocol is ttrpc, a compact gRPC variant; practical exploitation reuses containerd's client libraries pointed at the discovered abstract address rather than hand-rolling the wire format.
  • Fixes added authentication and restricted the shim socket exposure; patched containerd defeats it. Fingerprint the version before relying on this.

References#