A rounding error in the IPv6 fragment code hands root to anyone who can open a UDP socket inside a container
The bug lives in __ip6_append_data() on the paged/zero-copy allocation branch. When fraggap is non-zero, the linear skb region is sized too small while pagedlen is inflated by the same delta, so the subsequent copy overruns skb->end and writes attacker-controlled bytes into the trailing skb_shared_info structure. From there, standard kernel-heap grooming turns the OOB write into arbitrary read/write and credential replacement — game over. It was introduced by commit 773ba4fe9104 ('ipv6: avoid partial copy for zc') and made reachable via commit ce650a166335, so mainline kernels carrying both commits are exposed. Red Hat confirms RHEL 10 kernels are affected; RHEL 9 / OCP-on-RHEL-9 are not.
Vendor rating is HIGH 7.8 with an AV:L vector. That is technically correct on a single-tenant server (you need local + IPv6 UDP send). It materially understates the risk on any host that runs unprivileged user namespaces — which is the RHEL 10 default and every rootless-Podman, Kubernetes, and CI-runner deployment. Red Hat themselves branded the chain ipv6_frag_escape because the realistic exploitation model is *container tenant → host root, bypassing SELinux*. On a multi-tenant node, AV:L is a fiction — the attacker is a container workload, and 'local' means 'anyone who can push an image'.
5 steps from start to impact.
Land code execution inside a low-privilege context
- Local user shell OR container workload execution on the target kernel
- Target kernel is RHEL 10 (or upstream 6.x with commits
773ba4fe9104+ce650a166335)
- Requires attacker to already be past the perimeter and past image/pod admission
- Air-gapped single-tenant appliances offer no attacker foothold
Open an unprivileged user + net namespace
user.max_user_namespaces defaults to allow. The attacker calls unshare(CLONE_NEWUSER|CLONE_NEWNET) to obtain CAP_NET_ADMIN inside a fresh netns — this is what makes the whole class exploitable without root. In a container the netns already exists; only the ability to open an IPv6 socket is needed.user.max_user_namespaces> 0 OR container already provides netns- IPv6 stack enabled in the kernel (default)
- Hardened hosts that set
user.max_user_namespaces=0(Ubuntu-style AppArmor-restricted namespaces, Bottlerocket, GKE COS withkernel.unprivileged_userns_clone=0) break the pre-req - Seccomp profiles that deny
unshare(Docker default seccomp does NOT blockunsharebut does block CLONE_NEWUSER for non-root — many k8s profiles permit it)
Unexpected namespace change and sysdig userns rules fire; most orgs run them in audit-only.Trigger the OOB write via crafted UDPv6 sendmsg
sendmsg with MSG_MORE chained to MSG_SPLICE_PAGES (or exercises the NETIF_F_SG paged path) so that __ip6_append_data() enters the paged branch with a non-zero fraggap. The copy runs past skb->end and clobbers the adjacent skb_shared_info. PoC gadgets from prior skb_shared_info corruption bugs (e.g., the CVE-2023-42752 / CVE-2024-1086 style patterns) apply directly.- Ability to call
sendmsgon anAF_INET6/SOCK_DGRAMsocket - Kernel has zero-copy send path enabled (default y)
- Disabling IPv6 entirely (
ipv6.disable=1) makes the code path unreachable - Some hardened kernels compile out MSG_SPLICE_PAGES
Groom the SLUB cache and pivot to arbitrary R/W
skb_shared_info, the attacker sprays additional skbs to place a target object adjacent, uses the OOB to corrupt a frag_list pointer or destructor, and escalates to arbitrary kernel read/write. This is the well-worn skb corruption playbook; expect a public PoC within weeks based on Red Hat's severity and the class's history.- Deterministic SLUB layout (kmalloc-cg-* buckets)
- Ability to spray skbs — trivially satisfied from an unprivileged socket
- SLAB_HARDENED and CONFIG_RANDOM_KMALLOC_CACHES slow but do not stop the primitive
- Kernel Address Sanitizer builds catch it — not in production kernels
Overwrite creds → root on host → SELinux irrelevant
cred struct overwrite grants uid=0 in the init user namespace, then the attacker breaks out of the mount/pid namespaces via setns on /proc/1/ns/*. Because the escalation happens in kernel context, SELinux enforcement is bypassed — the LSM hook was never called. Result: host root on a Kubernetes worker or virtualization host.- Successful arbitrary kernel write from Step 4
- Read-only host root filesystem slows but does not stop the takeover
- Lockdown=confidentiality blocks some post-exploitation moves; rarely enabled
The supporting signals.
| In-the-wild | No confirmed exploitation as of 2026-07-23. Red Hat published RHSB-2026-009 naming the chain ipv6_frag_escape — the naming alone signals they expect weaponization. |
|---|---|
| Public PoC | No public PoC yet. Class is well understood — skb_shared_info OOB has been weaponized in prior kernel CVEs; expect a working exploit within 30–60 days. |
| EPSS | 0.00123 (~0.12%) — reflects absence of public exploit, not exploitability. Discount this metric for a fresh local-priv-esc. |
| KEV | Not listed as of 2026-07-23. Historically CISA adds Linux LPE chains only after in-the-wild abuse. |
| CVSS | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 7.8. S:U is wrong for the container-escape use case — scope changes when you jump namespaces. Corrected vector would push to ~8.8. |
| Affected | Mainline Linux with commits 773ba4fe9104 + ce650a166335. RHEL 10 kernels confirmed vulnerable. RHEL 9 and OpenShift on RHEL 9 are NOT affected. Ubuntu 24.04/24.10 HWE kernels and Fedora 40+ carry the offending commits — treat as vulnerable pending distro advisory. |
| Fixed | Upstream fix: ipv6: account for fraggap on the paged allocation path. Red Hat has shipped patched kernel packages for RHEL 10 — see RHSA linked in RHSB-2026-009. |
| Exposure | No external scanner data (local-only vector). Internal blast radius: every RHEL 10 host running rootless containers, every k8s worker node running RHEL 10 CoreOS derivatives, every multi-tenant CI runner. |
| Disclosed | 2026-07-04 (public), Red Hat RHSB published shortly after. |
| Reporter | Credit per upstream commit trailer; Red Hat Product Security coordinated RHSB-2026-009. |
noisgate verdict.
The decisive factor is the role multiplier: the affected component is the Linux kernel networking stack on hosts that run untrusted container workloads, where the documented chain is a full container-to-host escape with SELinux bypass — canonical fleet-scale blast radius. The vendor's AV:L / S:U framing understates this because 'local' in a multi-tenant cluster equals 'any pod'.
Why this verdict
- Role multiplier — Kubernetes worker / container host: chain succeeds; blast radius = host root → all co-tenant pods → cluster secrets → fleet compromise. This is the canonical deployment for RHEL 10 / CoreOS derivatives, well over the 10% installed-base threshold, so the floor is CRITICAL.
- Role multiplier — single-tenant bare-metal server: chain requires an already-authenticated local user; blast radius is host-local privilege escalation. Sits at HIGH on its own but does not pull the fleet-scale floor down.
- Role multiplier — hardened workstation (
user.max_user_namespaces=0): chain fails at Step 2. Irrelevant to fleet outcome but worth noting for asset-class carve-outs. - SELinux bypass is called out explicitly by Red Hat — one of the two controls most defenders lean on for container isolation is nullified in-kernel.
- No mitigating friction breaks the floor: disabling user namespaces breaks rootless Podman and many k8s features, so most fleets will not deploy it; EPSS is a lagging indicator and the exploit primitive (skb_shared_info OOB) is a well-trodden class.
Why not higher?
Not scored 10.0 because the bug is not remotely reachable — an attacker still needs a foothold (pod, container, shell). A wormable pre-auth network CVE would sit at 9.8–10.0. The 9.1 reflects near-certain full compromise given a container foothold, which every real k8s tenant provides.
Why not lower?
Not HIGH because on the canonical RHEL 10 container-host deployment the outcome is fleet-scale (host root + SELinux bypass on every worker), and that role is >10% of the affected install base. Downgrading to 7.8 would require ignoring the container-escape reality that Red Hat itself named the CVE around.
What to do — in priority order.
- Disable unprivileged user namespaces on RHEL 10 container hosts that do NOT need rootless Podman — Run
sysctl -w user.max_user_namespaces=0and persist via/etc/sysctl.d/99-cve-2026-53362.conf. Breaks Step 2 of the chain. Deploy within 3 days per the noisgate CRITICAL mitigation SLA. Verify withsysctl user.max_user_namespaces. - Block IPv6 on hosts that do not require it — Boot with
ipv6.disable=1or setnet.ipv6.conf.all.disable_ipv6=1. The vulnerable code path is only reachable via IPv6 UDP sendmsg. Practical for many appliance-class RHEL 10 hosts; NOT practical for dual-stack k8s clusters. Roll out within 3 days where feasible. - Enforce seccomp profiles that deny
unshare(CLONE_NEWUSER)for workloads that don't need it — Apply a k8s PodSecurityRestrictedprofile plus a seccomp profile denyingunsharewith the CLONE_NEWUSER flag for general-purpose workloads. Kills the netns pivot for pods that don't legitimately need it. Deploy via admission controller within 3 days. - Restrict node access to trusted images only — Enable image signing enforcement (cosign / Sigstore policy) and admission control (Kyverno/OPA) to shrink the attacker population that can land Step 1. Interim control while patch rollout proceeds.
- SELinux enforcing mode — Red Hat explicitly notes the exploit bypasses SELinux because credential mutation happens in kernel context before LSM hooks run.
- Egress firewalls / network segmentation — the attack is entirely local; no traffic leaves the host during exploitation.
- Rootless containers alone — rootless Podman actively *requires* the same unprivileged user namespaces that the exploit abuses.
- Perimeter WAF / IDS — bug is in the local networking stack, not exposed over the wire.
- EDR heuristics on process trees — the escalation path (skb corruption → cred overwrite) leaves no distinctive userspace process signal.
Crowdsourced verification payload.
Run this on each Linux host as root (or via your config management agent — Ansible/Salt/Chef). Invoke as sudo bash check-cve-2026-53362.sh. It checks kernel package version against Red Hat's fixed build for RHEL 10 and flags upstream kernels carrying the vulnerable commit range. Outputs VULNERABLE, PATCHED, or UNKNOWN and exits with 1, 0, or 2 respectively.
#!/usr/bin/env bash
# check-cve-2026-53362.sh — RHEL 10 / upstream Linux check for ipv6_frag_escape
set -u
verdict() { echo "$1"; exit "$2"; }
KREL=$(uname -r)
OS_ID=$(. /etc/os-release 2>/dev/null && echo "$ID")
OS_VER=$(. /etc/os-release 2>/dev/null && echo "$VERSION_ID")
# Fixed kernel build for RHEL 10 (per RHSA in RHSB-2026-009).
# Adjust FIXED_RHEL10 as new z-stream fixes ship.
FIXED_RHEL10="6.12.0-55.el10"
case "$OS_ID" in
rhel|almalinux|rocky)
if [[ "$OS_VER" == 10* ]]; then
# rpm version compare
CUR=$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' kernel | sort -V | tail -1)
if [[ -z "$CUR" ]]; then verdict "UNKNOWN: kernel rpm not found" 2; fi
LOW=$(printf '%s\n%s\n' "$CUR" "$FIXED_RHEL10" | sort -V | head -1)
if [[ "$LOW" == "$FIXED_RHEL10" ]]; then
verdict "PATCHED (kernel $CUR >= $FIXED_RHEL10)" 0
else
# also check user namespace exposure
UNS=$(sysctl -n user.max_user_namespaces 2>/dev/null || echo unknown)
verdict "VULNERABLE (kernel $CUR < $FIXED_RHEL10; user.max_user_namespaces=$UNS)" 1
fi
else
verdict "PATCHED (RHEL $OS_VER — not affected per RHSB-2026-009)" 0
fi
;;
ubuntu|debian|fedora|arch)
# Upstream heuristic: vulnerable code shipped in 6.10+; check if IPv6 zc paged path present
MAJ=$(echo "$KREL" | awk -F. '{print $1}')
MIN=$(echo "$KREL" | awk -F. '{print $2}')
if (( MAJ > 6 )) || { (( MAJ == 6 )) && (( MIN >= 10 )); }; then
# Look for fix string in changelog (best-effort)
if dmesg 2>/dev/null | grep -q 'account for fraggap'; then
verdict "PATCHED (fix string observed)" 0
fi
UNS=$(sysctl -n user.max_user_namespaces 2>/dev/null || echo unknown)
verdict "VULNERABLE (kernel $KREL in vulnerable range; user.max_user_namespaces=$UNS) — confirm against distro advisory" 1
else
verdict "PATCHED (kernel $KREL predates vulnerable commit range)" 0
fi
;;
*)
verdict "UNKNOWN: unsupported distro ($OS_ID $OS_VER); check manually against RHSB-2026-009" 2
;;
esac
If you remember one thing.
user.max_user_namespaces=0 to any host that does not require rootless Podman, and layer a seccomp unshare deny for workloads that don't need namespaces; (3) per the noisgate remediation SLA of ≤ 90 days, deploy the patched RHEL 10 kernel (or your distro's backport) fleet-wide, with priority ordering: multi-tenant nodes → shared CI → single-tenant servers → workstations. If a working PoC lands publicly before your rollout completes, treat KEV-equivalent and compress to hours, not days.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.