A Runtime That Runs Your Input
containerd is the engine under most of Kubernetes — the process that pulls your images, unpacks them, and starts your pods. On 1 July 2026, six CVEs landed against it at once, clustered around two surfaces the ecosystem barely threat-models: the CRI plugin's image-config handling and the newer checkpoint/restore (CRIU) import path.
The uncomfortable truth: a container runtime is an interpreter for attacker-influenceable input. An image config and a checkpoint archive are data you did not write, parsed by a process running as host root. Several of these bugs turn "pull this image" or "restore this checkpoint" into command execution on the node — no exploit inside the container required.
None of the six is a clean 10.0. That is exactly why they matter together: chains kill, not singles. Read on for how a 9.9 trust-corruption and an 8.8 pull-to-RCE compose into node takeover, with three more bugs removing the guardrails.
The Six · Identity Card
| CVE | CVSS | What it grants | Surface |
|---|---|---|---|
| CVE-2026-50195 | 9.9 | CRI checkpoint import → local image tag poisoning | checkpoint/restore |
| CVE-2026-53492 | 9.6 | checkpoint restore → CDI annotation smuggling (inject devices/mounts) | checkpoint/restore |
| CVE-2026-53488 | 8.8 | image-config LABEL → restart-monitor binary:// logger → host-root command exec on image pull | image-config |
| CVE-2026-46680 | 7.8 | user-ID handling bypass → runAsNonRoot evasion | image-config |
| CVE-2026-53489 | 6.5 | symlink following in checkpoint restore → arbitrary host CRI-log read | checkpoint/restore |
| CVE-2026-47262 | 5.5 | image-triggered runtime DoS via unbounded group parsing | image-config |
All disclosed 2026-07-01, affecting containerd through 1.7.14 (and the corresponding 1.6.x / 2.x branches — confirm the fixed release for your branch in the GHSA advisory). Boundary: Layer 4 Image / Layer 3 Node → crosses to host root = CRITICAL.
Where the Boundary Breaks
Two trust assumptions fail here, and both live below your YAML:
LABEL, user, group — are read by containerd and, in the vulnerable path, flow into runtime behaviour (a logger URI, the effective UID). Attacker-authored data steering a host-root process.LABEL or a poisoned checkpoint — the payload is configuration interpreted at runtime, not a vulnerable binary.runAsNonRoot is the control most teams lean on to neuter a bad image. CVE-2026-46680 makes the runtime mis-handle the ID and run it as root anyway — the guardrail reports green while it's open.What Each One Grants
Sorted by the primitive an attacker gains — the currency of a chain:
- Trust corruption → CVE-2026-50195 (9.9): importing a malicious checkpoint poisons a local image tag, so a trusted reference like
myapp:prodresolves to attacker content on that node. The trust anchor is now theirs. - Host-root code execution → CVE-2026-53488 (8.8): a crafted image-config
LABELis used to build a restart-monitorbinary://logger URI, and containerd executes it as host root when the image is used — pull-to-RCE, no container shell needed. - Escape primitives → CVE-2026-53492 (9.6): CDI (Container Device Interface) annotation smuggling during restore injects host devices and mounts into the container — the raw materials for a container escape.
- Policy bypass → CVE-2026-46680 (7.8): user-ID handling bypass defeats
runAsNonRoot, so the workload runs as root despite admission saying otherwise. - Disclosure → CVE-2026-53489 (6.5): symlink following in restore reads arbitrary host CRI log files — where tokens and secrets often leak.
- Denial / cover → CVE-2026-47262 (5.5): a crafted image triggers unbounded group parsing, crashing the runtime — useful as anti-forensics or to wedge a node.
The Kill Chain
Watch the currency compound. Each step spends the primitive the last one minted:
# 1. DELIVERY — a workload/CI/kubelet pulls a crafted image OR restores a crafted checkpoint # 2. 50195 — the import poisons the local tag: myapp:prod now = attacker layers on this node # 3. 53488 — that image's LABEL drives restart-monitor's binary:// logger: # containerd runs the attacker's command AS HOST ROOT when the image is used # 4. 53492 — CDI smuggling mounts /dev + host paths into the container (escape kit) # 5. 46680 — runAsNonRoot is bypassed: the container is root, so nothing inside constrains it # 6. 53489/47262 — read host CRI logs for tokens; crash the runtime to cover tracks # => host root on the node → kubelet creds → every pod's secrets → lateral movement
Individual risk: LOW + MEDIUM + HIGH. Combined risk: CRITICAL node compromise. Why the chain beats the sum: 50195 corrupts trust, 53488 spends it for host-root execution, and 53492/46680 remove the two controls (isolation, non-root) that would otherwise contain the blast.
Propagation
Prerequisites
runAsNonRoot / image scanning as the main control — both are what these bugs defeat.Walkthrough: Pull → Host Root
What a defender should be able to reproduce in a lab (do not run in production):
# attacker crafts an image whose config LABEL encodes a binary:// logger target # (the field the vulnerable restart-monitor path builds its logger URI from) # and/or a checkpoint archive that remaps a trusted local tag: # on the node, the "trusted" tag now resolves to attacker content (50195): ctr -n k8s.io images ls | grep myapp:prod # digest changed out from under you # when that image is used, containerd's restart-monitor executes the logger # target AS HOST ROOT (53488) — e.g. a reverse shell or an implant drop: # (no exec into the container; the runtime does it for you) # CDI smuggling from the restore injects host access (53492): # annotations request devices/mounts the pod spec never asked for crictl inspect <container> | jq '.info.runtimeSpec.mounts, .status.devices'
Detection
containerd / restart-monitor that aren't shims or your known helpers — the 53488 signature is command execution parented by the runtime. High.LABEL values that look like URIs/paths. High.import/restore calls at all — especially from identities that shouldn't checkpoint; CDI annotations appearing on restored containers. High.runAsNonRoot: true whose process is actually UID 0 at runtime (46680) — reconcile admission intent against real effective UID. Medium.- rule: containerd executed a host command (restart-monitor logger abuse)
desc: A process was spawned directly by containerd/restart-monitor - possible CVE-2026-53488
condition: >
spawned_process and proc.pname in (containerd, containerd-restart-monitor)
and not proc.name in (containerd-shim, runc, containerd-shim-runc-v2)
output: >
Host command spawned by container runtime (parent=%proc.pname cmd=%proc.cmdline
user=%user.name image=%container.image.repository)
priority: CRITICAL
tags: [container_escape, cve-2026-53488, T1610]
- rule: CRI checkpoint restore / import observed
desc: A checkpoint was imported or restored - the 50195/53492/53489 surface
condition: ka.verb in (create,update) and ka.uri contains "checkpoint"
output: "CRI checkpoint op (user=%ka.user.name uri=%ka.uri)"
priority: WARNING
source: k8s_audit
tags: [cve-2026-50195, cve-2026-53492]Remediation
Immediate — patch the runtime (the actual fix):
# which nodes are on a vulnerable runtime? kubectl get nodes -o wide \ -o custom-columns=NODE:.metadata.name,RUNTIME:.status.nodeInfo.containerRuntimeVersion # on a node: confirm the patched build for your branch (see the GHSA advisory) containerd --version # must be past 1.7.14 (or fixed 1.6.x / 2.x)
Immediate — shut the higher-severity door:
- Disable / tightly gate CRI checkpoint-restore unless you actively use forensic checkpointing. It carries three of the six (50195, 53492, 53489). If you need it, restrict the kubelet checkpoint API and never import untrusted archives.
- Sign and verify images at admission (cosign + Kyverno
verifyImages) so a poisoned local tag fails verification instead of running.
Permanent — never let the runtime be the only wall:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: {name: containerd-hardening}
spec:
validationFailureAction: Enforce
rules:
- name: only-signed-images
match: {any: [{resources: {kinds: [Pod]}}]}
verifyImages:
- imageReferences: ["registry.internal/*"]
attestors: [{entries: [{keys: {publicKeys: "<cosign pubkey>"}}]}]
- name: no-privileged-no-hostpath
match: {any: [{resources: {kinds: [Pod]}}]}
validate:
message: "privileged containers and hostPath mounts are forbidden"
pattern:
spec:
=(volumes): [{=(hostPath): {path: "!/"}}]
containers: [{=(securityContext): {=(privileged): "false"}}]- Don't trust
runAsNonRootalone (46680 defeats it) — pair it with a non-root base image, a read-only root filesystem, and a restrictive seccomp/AppArmor profile. - Pin images by digest, use an internal mirror, and treat registry/CI credentials as node-root-equivalent.
Runtime CVEs are the ones that invalidate your whole threat model, because they live under every abstraction you reason about — you cannot NetworkPolicy or RBAC your way out of a runtime that runs an attacker's LABEL as root. The July 2026 containerd set is a preview of where this goes: as checkpoint/restore and richer image metadata expand what the runtime interprets, the runtime's input becomes attack surface. Patch fast, treat the checkpoint/restore import path as hostile until proven otherwise, verify image signatures so a poisoned tag can't run, and keep a second, independent gate that a runtime-level bug can't switch off.
— k8sec Security Research
Know which nodes run a vulnerable runtime — and what a poisoned image could reach.
K8SEC inventories containerd/runc versions per node, flags where checkpoint-restore is reachable, and maps the image → host → cluster path so a runtime CVE is a dashboard, not a 2 a.m. page.
Explore K8SECSources: containerd security advisories (GHSA) for CVE-2026-50195, -53492, -53488, -46680, -53489, -47262 (all 2026-07-01), cross-referenced via cve.tools; CDI and CRIU checkpoint/restore documentation; MITRE ATT&CK for Containers (T1610 Deploy Container, T1195 Supply Chain, T1611 Escape to Host). CVSS values and the affected ≤ 1.7.14 boundary are from the advisories; exact fixed release numbers should be confirmed per branch in the GHSA. The kill-chain and walkthrough are a defender-side reconstruction, not attacker code.
