Key Takeaway
CVE-2026-46680 lets a crafted image start as root under containerd even when a Kubernetes Pod sets runAsNonRoot, because an oversized numeric User is resolved as a username. Patch to 1.7.32, 2.0.9, 2.2.4 or 2.3.1 or later; the 2.1 line gets no fix. Until then, set an explicit numeric runAsUser in every Pod.
I read GHSA-fqw6-gf59-qr4w looking for one thing, whether my clusters were in scope, and the answer sat in a single sentence. A container started with a numeric User too large for 32 bits is treated as a username, and a crafted image can map that string to root in its own /etc/passwd. The Pod spec still says runAsNonRoot: true. The container runs as UID 0 anyway.
This post covers the advisory published on 20 May 2026 and tracked as CVE-2026-46680, rated Moderate by GitHub. Everything below comes from the advisory, the containerd release policy and releases page, the earlier related advisory, and Kubernetes' own API and Pod Security Standards sources, all re-read on 17 October 2026. I did not build or run a malicious image, and nothing here is an exploit; it is defender material.
What the advisory actually says
The advisory is titled containerd user ID handling bypass allows runAsNonRoot evasion. Its claim is narrow: containers started with a numeric User value that cannot be parsed as a 32-bit integer are treated as a username instead. If the image's /etc/passwd maps that large numeric string to root, the container runs as UID 0, which can bypass the Kubernetes runAsNonRoot restriction.
Three scoping details matter. GitHub rates it Moderate. The reporter credited is Lei Wang (ssst0n3), who used the containerd security policy. And the crafted image has to be the one you actually run: the advisory's own workaround begins with using only trusted images and limiting image import to trusted users, which suggests the realistic threat is a poisoned or look-alike image reaching a cluster that treats runAsNonRoot as its safety net.
Why a policy check and a runtime can disagree
Kubernetes documents runAsNonRoot as a promise about the image: if true, the kubelet validates the image at runtime to ensure it does not run as UID 0, and fails to start the container if it does. runAsUser is different; it is the UID for the container entrypoint and defaults to the user in the image metadata when unspecified. As I read it, two components therefore interpret the image's User field, one to validate and one to start the process, and the advisory describes the runtime side resolving an oversized number as a name.
That is why the Pod-level fix works. When runAsUser is set explicitly, the image's USER directive no longer decides the UID; the advisory lists exactly this as a workaround. Here are the vulnerable shape and the safer one side by side.
# Wrong: relies on the image's USER directive being honest. On an affected
# containerd, an oversized numeric USER can resolve to root through the
# image's own /etc/passwd, and runAsNonRoot alone did not stop it.
apiVersion: v1
kind: Pod
metadata:
name: api-wrong
spec:
securityContext:
runAsNonRoot: true
containers:
- name: api
image: registry.example.com/team/api:1.4.2
---
# Right: an explicit numeric UID overrides the image metadata (the advisory's
# workaround), and runAsNonRoot stays on as a second, independent check.
apiVersion: v1
kind: Pod
metadata:
name: api-right
spec:
securityContext:
runAsUser: 10001
runAsGroup: 10001
runAsNonRoot: true
containers:
- name: api
image: registry.example.com/team/api:1.4.2
securityContext:
allowPrivilegeEscalation: falseAffected and fixed versions across four release lines
The advisory gives affected and fixed versions for the 1.7 module and the v2 module. I merged them with the support status from containerd's RELEASES.md and the latest patch numbers shown on the releases page, because a fixed version is only useful if the line is still maintained.
| Release line | Affected from | Fixed in | Status and latest patch |
|---|---|---|---|
| 1.7 | 1.7.27 | 1.7.32 | End of life since 30 September 2026. Releases page shows 1.7.36. |
| 2.0 | 2.0.4 | 2.0.9 | LTS. Extended support to March 2027 from two named maintainers. Latest 2.0.14. |
| 2.1 | Inside the v2 range from 2.0.4 | No fix | End of life since 3 July 2026. |
| 2.2 | Inside the v2 range from 2.0.4 | 2.2.4 | Active. End of life 6 November 2026. Latest 2.2.10. |
| 2.3 | Inside the v2 range from 2.0.4 | 2.3.1 | LTS until 30 April 2028. Latest 2.3.7. |
Read the last column before the third. By the policy page, 1.7 reached end of life on 30 September 2026, so a 1.7 node can be patched to 1.7.32 or later but is not a line to stay on. The 2.1 line ended on 3 July 2026 and the advisory says it gets no fix, so the only route is to move to 2.2 or 2.3. The policy says upgrades are supported between sequential minor releases and directly between sequential LTS releases, for example 1.7 to 2.3.
I would patch to the newest patch in your line rather than the minimum fixed version. The releases page shows 2.0.14, 2.2.10 and 2.3.7 on 13 October, each with a hardening change that filters ID-mapping labels from image annotations during unpack, which 1.7.36 does not include. I could not confirm that change is related to this CVE, so treat it as a reason to take the latest patch, not as part of the fix.
The same bug class appeared two years ago
This is not the first advisory in this corner. GHSA-265r-hfxg-fhmg (CVE-2024-40635), also Moderate, says containers launched with a User set as a UID:GID larger than the maximum 32-bit signed integer could overflow, so the container ultimately runs as root. It was fixed in 1.6.38, 1.7.27 and 2.0.4, with the same workaround: trusted images only, and only trusted users importing them.
The new advisory's affected ranges start at 1.7.27 and 2.0.4, the same releases that fixed the earlier one, and it lists the earlier advisory as a reference. The sources do not say whether the earlier fix opened the new path or merely left a neighbouring one open, and I will not guess. The practical reading is simpler: turning a User string into a UID has been wrong twice, so it should never be your only line of defence.
Four mitigations and what each one really covers
The advisory offers three workarounds and the maintainers add a Kubernetes note. I put them next to the permanent fix so the trade is visible.
| Mitigation | What it relies on | Limit |
|---|---|---|
| Patch the runtime | containerd at or above the fixed version on every node | Needs a node rollout, and 2.1 has no fixed release to roll to. |
| Explicit numeric runAsUser | The Pod securityContext, which the advisory says overrides the image USER | Must be set on every workload; an unset one stays exposed on an affected node. |
| Kubernetes 1.34 or later | The advisory says these versions appear to enforce runAsNonRoot regardless | Hedged by the maintainers themselves; not something to plan around. |
| Trusted images and restricted import | Registry and image import permissions | Reduces who can supply a crafted image; it is not a fix. |
Only the first two change what happens on a node you control. The third is an observation, and the fourth is hygiene you should already have.
Do both of the first two. The Pod change costs a manifest edit and protects you in the window while nodes drain and roll; the patch then removes your dependence on that edit being present everywhere.
Auditing a cluster for exposure
Three questions decide your exposure: which nodes run which containerd, which containers leave the UID to the image, and which images carry an oversized numeric User. The commands below answer each from kubectl and, for the third, crane.
# 1. Runtime version per node. Compare against the fixed versions:
# 1.7.32, 2.0.9, 2.2.4, 2.3.1. A node on containerd://1.7.31 is affected.
kubectl get nodes -o custom-columns=NAME:.metadata.name,RUNTIME:.status.nodeInfo.containerRuntimeVersion
# 2. Containers with no runAsUser at pod or container level, so the image's
# own USER decides the UID. These are the workloads this advisory touches.
kubectl get pods -A -o json | jq -r '
.items[] | . as $p
| $p.spec.containers[]
| select($p.spec.securityContext.runAsUser == null and .securityContext.runAsUser == null)
| [$p.metadata.namespace, $p.metadata.name, .name, .image] | @tsv'
# 3. Images whose configured User is numeric and above the signed 32-bit limit.
# The advisory says "cannot be parsed as a 32-bit integer" and the 2024 one
# says "signed", so 2147483647 is the conservative line. Names and empty
# values are normal and skipped. Init containers are not covered here.
kubectl get pods -A -o jsonpath='{range .items[*].spec.containers[*]}{.image}{"\n"}{end}' | sort -u |
while read -r img; do
user=$(crane config "$img" 2>/dev/null | jq -r '.config.User // ""')
uid=${user%%:*} # drop an optional :GID suffix
case "$uid" in
''|*[!0-9]*) ;; # empty or a name: not this check
*) if [ "${#uid}" -gt 10 ] || [ "$uid" -gt 2147483647 ]; then
echo "SUSPECT $img User=$user"
fi ;;
esac
doneTreat the third check as a lead, not proof. A numeric User that looks too large is only dangerous with a matching entry in the image's /etc/passwd, and I did not test for one. It also skips init containers and reads whichever platform crane resolves, so extend it if your fleet is multi-architecture.
Where this goes wrong
The expensive mistake is treating runAsNonRoot: true as a control on its own. The Restricted profile of the Pod Security Standards requires runAsNonRoot to be true and allows runAsUser to be any non-zero value or undefined. A Pod that leaves runAsUser unset therefore passes admission, and admission only sees the spec, not the image's /etc/passwd. On an affected runtime that Pod can start as root with every check green.
The second trap is the cost of the fix. Pinning runAsUser means the process no longer runs as the user the image was built for, so files owned by the image's own user may stop being readable or writable. That is my reasoning from how UIDs work, not a statement in the sources, so test each workload before rolling the change across the fleet, and prefer images built to run as a known numeric UID.
A node reporting containerd 2.1.x is past end of life and, per the advisory, gets no fix. Patching in place is not possible. Plan the move to 2.2 or 2.3 and, until then, set runAsUser explicitly on every workload scheduled to those nodes.
Do not read the Kubernetes 1.34 line as a mitigation either. The advisory only says versions from 1.34 appear to enforce runAsNonRoot correctly regardless of this bug. Appear is the maintainers' own hedge, and I could not find the underlying change in the sources I read.
Finally, severity labels can mislead in both directions. GitHub rates this Moderate, and other trackers publish different scores, so I would not rank it from a number. A cluster that runs third-party images and relies on runAsNonRoot for isolation should treat it as a priority; one that runs only images it builds can reasonably schedule it with the next runtime upgrade.
What to do on Monday
In order, each step small enough to finish in a day.
- Run the node query and list every node below a fixed version for its line, noting any on 2.1.x.
- Add an explicit numeric runAsUser to workloads that lack one, starting with namespaces that pull from public registries or accept third-party images.
- Roll nodes to the newest patch in the line, which the releases page showed as 1.7.36, 2.0.14, 2.2.10 and 2.3.7, and schedule the move off 1.7 and 2.1.
- Run the image audit, check where each suspect image came from, and restrict who can import images into your registry and node runtime.
- Re-run the node query after the rollout and record the date and the result so the next audit has a baseline.
Version numbers and dates here were read from the sources on 17 October 2026. The releases page moves, so re-check the latest-patch column before relying on it.
A security context field states an intention; the container runtime is what carries it out. Pin the UID explicitly, patch containerd to a fixed release in your line, and retire the node pools you cannot upgrade, the 2.1 ones first.
Sources and further reading
- containerd GHSA-fqw6-gf59-qr4w: the runAsNonRoot evasion advisory (CVE-2026-46680)
- containerd GHSA-265r-hfxg-fhmg: the earlier user ID advisory (CVE-2024-40635)
- containerd RELEASES.md: release lines and support policy
- containerd releases page: latest patch versions
- Kubernetes docs: Pod Security Standards, Restricted profile
- Kubernetes API types: SecurityContext runAsNonRoot and runAsUser field documentation

