← Back to Feed CACHED · 2026-06-26 16:46:02 · CACHE_KEY CVE-2026-46331
CVE-2026-46331 · CWE-787 · Disclosed 2026-06-16

In the Linux kernel

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A kernel packet-editor that promised to make memory writable forgot to count the bytes — and the leftover writes land on cached copies of root binaries

tcf_pedit_act() in net/sched/act_pedit.c precomputes a single COW range using tcfp_off_max_hint and calls skb_ensure_writable() once before the per-key write loop. The hint doesn't include the runtime header offset that *typed* keys add at execution time, so a crafted pedit action writes past the COW'd region into the shared page cache. The end state: an attacker overwrites the in-memory page of a setuid binary (the public exploit targets /bin/su), and the next exec gives them root — all without ever touching the on-disk file. Affected: mainline Linux through June 16 2026, including kernels shipped on RHEL 10 (6.12.0-228.el10), RHEL 9 / 8 backports, Debian 13 (6.12.90+deb13.1), and Ubuntu 24.04.4 (6.17.0-22).

There is no NVD CVSS yet; Red Hat called it *Important*, which is the right neighborhood but underplays the operational reality. A working, author-signed PoC (Massimiliano Oldani, sgkdev/packet_edit_meme) landed within 24 hours of disclosure. It's a clean, deterministic LPE on every default-configured RHEL and Debian box with unprivileged user namespaces — which is most of them. Local-only is the only thing keeping this out of CRITICAL.

"Local-only, but a turnkey public PoC walks an unprivileged user to root by poisoning cached /bin/su. Patch the fleet now."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Land local code execution as any unprivileged user

The attacker needs a shell — RCE in a web app, SSH from a leaked credential, a CI runner job, a malicious container escape with namespace privs, or a phished developer. No special group membership required.
Conditions required:
  • any local user account or RCE foothold
Where this breaks in practice:
  • pure RCE-as-www-data on a hardened jump box may not have a writable tmp to drop the exploit
Detection/coverage: EDR sees the foothold, not the bug — Falco, CrowdStrike, SentinelOne all catch standard initial-access tradecraft
STEP 02

Enter a user namespace to acquire namespace-local CAP_NET_ADMIN

The PoC calls unshare(CLONE_NEWUSER|CLONE_NEWNET) to get CAP_NET_ADMIN inside the new netns. This is the gate: it requires kernel.unprivileged_userns_clone=1 (default on RHEL 10, Debian, mainstream distros). Ubuntu 24.04 layers an AppArmor profile that the public PoC already bypasses.
Conditions required:
  • unprivileged_userns_clone enabled
  • no seccomp filter blocking unshare/clone3
Where this breaks in practice:
  • hardened images (CIS L2, Bottlerocket, Talos) often disable unprivileged user namespaces
  • containers with default Docker seccomp profile cannot unshare(CLONE_NEWUSER) from inside the container
Detection/coverage: auditd/SYSCALL=unshare with CLONE_NEWUSER from non-root is a high-signal event most shops do not log
STEP 03

Configure a tc qdisc with a malicious pedit action

Inside the namespace, the exploit uses tc qdisc add + tc filter add action pedit with a typed key whose runtime header offset pushes the write past the precomputed COW boundary. act_pedit autoloads on first use.
Conditions required:
  • act_pedit kernel module present (default in all major distro kernels)
  • no module-loading lockdown
Where this breaks in practice:
  • modules_disabled=1 or a strict module signing + denylist for act_pedit blocks autoload
  • kernel-lockdown integrity mode does NOT block this — pedit is signed
Detection/coverage: kauditd module-load events for act_pedit from a user-namespace context are anomalous
STEP 04

Trigger the partial-COW write to corrupt a cached setuid binary page

The exploit reads /bin/su into the page cache, sends a crafted packet through the tc datapath, and the unbounded write lands on the cached page. Because the skb shares memory with the page cache for that read, the binary's in-memory image is rewritten with an exec-shellcode equivalent. The on-disk file is unchanged — AIDE/Tripwire see nothing.
Conditions required:
  • a setuid-root binary cached in memory (su, sudo, pkexec, mount — pick any)
Where this breaks in practice:
  • SELinux in enforcing mode does NOT stop the write itself (page cache is below the LSM)
  • tmpfs-only roots or read-only rootfs with dm-verity (ChromeOS, Bottlerocket, Talos) defeat the cached-binary pivot
Detection/coverage: no scanner detects this at write-time; the only signal is the prior tc/pedit syscall pattern
STEP 05

Execute the poisoned binary, get UID 0

The attacker runs su (or whichever target was poisoned). The kernel maps the corrupted page, jumps to the shellcode running as root, and returns a clean root shell. End state: full local privilege escalation, persistence trivially achievable.
Conditions required:
  • the poisoned binary is executable by the attacker
Where this breaks in practice:
  • IMA-appraisal in enforce mode revalidates pages on exec and refuses — but almost nobody runs IMA-appraisal in prod
Detection/coverage: EDR sees an unexpected su → root child process tree; this is the best post-exploit signal
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo confirmed mass exploitation as of 2026-06-27, but a working public PoC has been live for 10 days. Treat as imminent.
Proof-of-conceptsgkdev/packet_edit_meme by Massimiliano Oldani, published 2026-06-17 — deterministic, ships ready-to-run.
EPSS0.00291 (low percentile) — EPSS lags local-LPE bugs badly; ignore it for prioritization.
CISA KEVNot listed as of 2026-06-27.
CVSS vectorNo NVD vector published. Realistic vector: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H → ~8.4 (scope-change because the page cache is shared across users).
Affected versionsMainline Linux through 2026-06-16; RHEL 10 6.12.0-228.el10, RHEL 9 / 8 backport kernels, Debian 13 6.12.90+deb13.1, Ubuntu 24.04.4 6.17.0-22. Any kernel with act_pedit and unprivileged user namespaces.
Fixed versionsRHEL 10: RHSA-2026:27288, RHEL 9: RHSA-2026:27789, RHEL 8 / AlmaLinux 8: RHSA-2026:27353 / ALSA-2026:27353, Debian 13: DSA-6355-1 (2026-06-21), Ubuntu USN pending, mainline + stable backports merged 2026-06-16.
Exposure dataNot an internet-reachable bug — GreyNoise/Shodan irrelevant. Population at risk = every Linux host with unprivileged_userns_clone=1 and act_pedit loadable, i.e. *the majority of enterprise Linux*.
Disclosure2026-06-16 via the Linux kernel security tree commit + linux-cve-announce.
Reporter / fixPatch authored by net/sched maintainers; PoC and write-up by Massimiliano Oldani.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to HIGH (8.4/10)

This is a local privilege escalation with a public, turnkey PoC that converts any unprivileged shell into root on default-configured RHEL, Debian, and Ubuntu kernels. The single decisive factor is local-only attack vector — if it were remote it would be CRITICAL; because every chain starts from an existing foothold, HIGH is the correct bucket.

HIGH exploitability — PoC is public, deterministic, and author-verified
HIGH patch availability — RHEL, Debian, mainline all shipped within 5 days
MEDIUM blast radius — depends on whether the host is multi-tenant or single-user

Why this verdict

  • Public weaponized PoC within 24h of disclosure. sgkdev/packet_edit_meme is a one-shot LPE, not a crash repro. The bar to use it is git clone && make && ./exploit.
  • Default-configured exploit surface. RHEL 10 and Debian 13 ship with unprivileged_userns_clone=1 and act_pedit autoloadable — no hardening checkbox to flip first. This is not a niche feature.
  • Friction adjustment — local only. AV:L is the one and only thing pulling this off CRITICAL. The attacker must already be on the box (RCE, stolen creds, malicious insider, CI runner). That's a real precondition and it puts the verdict at HIGH, not 10.0.
  • Friction adjustment — container default-deny. Docker/Kubernetes default seccomp blocks unshare(CLONE_NEWUSER) from inside a container, so the typical webapp-RCE-in-a-pod path does NOT auto-chain. Shared-tenancy bare-metal and VM hosts have no such gate.
  • Role multiplier — multi-tenant Linux (shared dev boxes, jump hosts, bastions, JupyterHub, HPC login nodes, university shells): chain succeeds, blast radius = full host compromise of a host that holds many users' credentials and SSH keys → lateral pivot to dozens of downstream systems. Floor = HIGH.
  • Role multiplier — container hosts / Kubernetes worker nodes: if the attacker is *on the node* (not in a pod), chain succeeds and yields node-root → kubelet credentials → cluster-wide pivot. This is a high-value role but reaching the node requires a prior container escape, so it sets the ceiling at HIGH not CRITICAL.
  • Role multiplier — single-tenant app servers and developer workstations: chain succeeds; impact is local root on one box. Still HIGH because of pivot potential to credentials cached on that host (cloud SDK keys, kubeconfig, browser cookies).
  • No high-value role canonically owned by act_pedit. Unlike a hypervisor or domain controller bug, this is a kernel LPE — the role multiplier doesn't push to a CRITICAL floor because the affected *component* isn't a fleet-control plane on its own.

Why not higher?

CRITICAL requires either remote exploitability or a chain that ends in fleet-scale compromise from a single act. This bug requires a pre-existing local foothold and ends in single-host root. The realistic CVSS tops out around 8.4 with scope-change accounting for page-cache cross-user impact — that's the top of HIGH, not CRITICAL.

Why not lower?

MEDIUM is wrong because there is a public, deterministic, weaponized PoC against default distro configurations and the chain reliably ends in root. EPSS of 0.00291 reflects scanner blindness to LPE bugs, not actual risk. Any environment with shared Linux hosts, CI runners, or untrusted local code (i.e. almost every enterprise) must treat this as urgent.

05 · Compensating Control

What to do — in priority order.

  1. Disable unprivileged user namespaces fleet-wide — Set kernel.unprivileged_userns_clone=0 (Debian/Ubuntu) or user.max_user_namespaces=0 (RHEL) via sysctl drop-in. Kills the namespace-local CAP_NET_ADMIN path that gates the exploit. Deploy within 3 days per noisgate mitigation SLA; verify with Ansible/Salt that the sysctl sticks across reboot. Caveat: breaks rootless Podman, Chrome sandbox, Flatpak — test on a workstation pilot ring first.
  2. Blocklist the act_pedit kernel module — Drop install act_pedit /bin/false into /etc/modprobe.d/noisgate-cve-2026-46331.conf and rmmod act_pedit if already loaded. No legitimate workload outside of advanced QoS shaping uses pedit. Deploy within 3 days; verify with modprobe -n -v act_pedit showing the install line.
  3. Apply the vendor kernel patch and reboot — Stage RHSA-2026:27288 / 27789 / 27353, DSA-6355-1, and the corresponding Ubuntu USN through your normal kernel-rollout pipeline. Remediation deadline is 90 days per noisgate remediation SLA, but for any multi-tenant host (bastion, jump, dev shell, CI runner) accelerate to 14 days.
  4. Enable kpatch / kernel live patching where available — RHEL kpatch and Ubuntu Livepatch both ship this fix as a no-reboot patch. Best path for hosts you cannot reboot inside the 3-day mitigation window.
  5. Hunt for prior abuse in audit logs — Search auditd for unshare(CLONE_NEWUSER|CLONE_NEWNET) followed by tc filter add from non-root UIDs in the last 30 days. Any hit on a production host is incident-response material.
What doesn't work
  • SELinux / AppArmor enforcing — the partial-COW write happens below the LSM hook; the kernel sees a legitimate skb write. Ubuntu's AppArmor profile narrows the exploit surface but the public PoC already bypasses it.
  • File integrity monitoring (AIDE, Tripwire, OSSEC FIM) — the on-disk binary is never modified. Only the cached page is poisoned. FIM will not alert.
  • Network controls (firewalls, segmentation, WAF, IDS) — this is a local bug. No packet ever leaves the host.
  • Disabling root login or su — the exploit picks any setuid-root binary cached in memory. sudo, pkexec, mount, passwd all work equally well as targets.
  • EDR signatures for the PoC binary name — recompile breaks the hash; the underlying primitive is generic.
06 · Verification

Crowdsourced verification payload.

Run on each target Linux host as root (or via your config-mgmt agent). Example: sudo ./check-cve-2026-46331.sh. The script checks running kernel version against the patched kernel for the detected distro and reports VULNERABLE / PATCHED / UNKNOWN.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate verification: CVE-2026-46331 (net/sched pedit partial COW)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u

KVER="$(uname -r)"
DISTRO="unknown"
if [ -r /etc/os-release ]; then
  . /etc/os-release
  DISTRO="${ID:-unknown}"
  DVER="${VERSION_ID:-}"
fi

echo "[noisgate] kernel=$KVER distro=$DISTRO $DVER"

# Helper: compare dotted versions; returns 0 if $1 >= $2
ver_ge() { [ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | tail -1)" = "$1" ]; }

verdict="UNKNOWN"
reason="distro/kernel not in known-patched table"

case "$DISTRO" in
  rhel|centos|almalinux|rocky)
    # RHEL 10: fixed in RHSA-2026:27288 (>= 6.12.0-228.4.el10 or rebuild post-fix)
    # RHEL 9:  fixed in RHSA-2026:27789
    # RHEL 8:  fixed in RHSA-2026:27353
    if echo "$KVER" | grep -q 'el10'; then
      FIXED="6.12.0-228.4.el10"
    elif echo "$KVER" | grep -q 'el9'; then
      FIXED="5.14.0-580.el9"
    elif echo "$KVER" | grep -q 'el8'; then
      FIXED="4.18.0-620.el8"
    else FIXED=""; fi
    if [ -n "$FIXED" ]; then
      RUN="$(echo "$KVER" | sed 's/\.\(x86_64\|aarch64\|ppc64le\|s390x\)$//')"
      if ver_ge "$RUN" "$FIXED"; then verdict="PATCHED"; reason="$RUN >= $FIXED"; else verdict="VULNERABLE"; reason="$RUN < $FIXED"; fi
    fi ;;
  debian)
    # Debian 13: DSA-6355-1, fixed kernel 6.12.90+deb13.2 or later
    FIXED="6.12.90+deb13.2"
    if ver_ge "$KVER" "$FIXED"; then verdict="PATCHED"; reason="$KVER >= $FIXED"; else verdict="VULNERABLE"; reason="$KVER < $FIXED"; fi ;;
  ubuntu)
    # Ubuntu 24.04: USN pending; treat anything < 6.17.0-23 as VULNERABLE
    FIXED="6.17.0-23"
    if ver_ge "$KVER" "$FIXED"; then verdict="PATCHED"; reason="$KVER >= $FIXED"; else verdict="VULNERABLE"; reason="$KVER < $FIXED"; fi ;;
esac

# Mitigation indicators (downgrade VULNERABLE -> MITIGATED-style note)
MITIGATED=0
USERNS="$(sysctl -n kernel.unprivileged_userns_clone 2>/dev/null || echo unknown)"
MAXNS="$(sysctl -n user.max_user_namespaces 2>/dev/null || echo unknown)"
if [ "$USERNS" = "0" ] || [ "$MAXNS" = "0" ]; then MITIGATED=1; fi
if grep -qsE '^(install|blacklist)\s+act_pedit' /etc/modprobe.d/*.conf 2>/dev/null; then MITIGATED=1; fi

echo "[noisgate] verdict=$verdict ($reason)"
echo "[noisgate] unprivileged_userns_clone=$USERNS max_user_namespaces=$MAXNS act_pedit_blocklisted=$( [ $MITIGATED -eq 1 ] && echo yes || echo no )"

case "$verdict" in
  PATCHED)    exit 0 ;;
  VULNERABLE) [ $MITIGATED -eq 1 ] && echo '[noisgate] NOTE: kernel vulnerable but compensating control present'; exit 1 ;;
  *)          exit 2 ;;
esac
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: push kernel.unprivileged_userns_clone=0 and the act_pedit modprobe blocklist to every Linux host via your config-management plane — that's the noisgate mitigation SLA of 3 days for a HIGH-with-public-PoC bug, and you can move it that fast because both controls are sysctl/modprobe drop-ins. Stage RHSA-2026:27288/27789/27353, DSA-6355-1, and the Ubuntu USN into your kernel-rollout pipeline behind the noisgate remediation SLA of 90 days, but compress that to 14 days on every multi-tenant host (bastions, jump boxes, CI runners, dev shells, JupyterHub, HPC login nodes) where the LPE blast radius is worst. Use kpatch / Livepatch for the hosts you cannot reboot. Ignore EPSS on this one — it does not see local-LPE chains; the PoC is public and works out of the box.

Sources

  1. Ubuntu Security — CVE-2026-46331
  2. TuxCare — pedit-COW analysis
  3. Red Hat — RHSA-2026:27704
  4. GitHub Advisory GHSA-cr2w-747q-47qc
  5. The Hacker News — pedit COW root exploit coverage
  6. Mallory vulnerability record
  7. PT Security dbugs entry
  8. OpenCVE — CVE-2026-46331
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.