Linux Kernel Local Privilege Escalation — Fragnesia, Dirty Frag, Copy-Fail, ssh-keysign-pwn, nf_tables Typo, CIFSwitch, DirtyClone
Linux Kernel Local Privilege Escalation — Fragnesia, Dirty Frag, Copy-Fail, ssh-keysign-pwn, nf_tables Typo, CIFSwitch, DirtyClone
Severity: Critical | Last Updated: 2026-07-02 | Revision: userns workaround corrected (see Revision History)
CVEs: CVE-2026-46300, CVE-2026-43284, CVE-2026-31431, CVE-2026-46333, CVE-2026-23111, CVE-2026-46243, CVE-2026-43503
CVE | Codename | Disclosed | CVSS | Patch Available | CISA KEV |
|---|---|---|---|---|---|
CVE-2026-46300 | Fragnesia | 2026-05-13 | 7.8 High | Yes | No |
CVE-2026-43284 | Dirty Frag | 2026-05-13 | 7.8 High | Yes | No |
CVE-2026-31431 | Copy-Fail | 2026-04-29 | 7.8 High | Yes | Yes — deadline 2026-05-15 |
CVE-2026-46333 | ssh-keysign-pwn | 2026-05-15 | 7.8 High | Yes | No |
CVE-2026-23111 | nf_tables Typo | 2026-06-08 | 7.8 High | Yes | No |
CVE-2026-46243 | CIFSwitch | 2026-05-27 | 7.8 High | Yes | No |
CVE-2026-43503 | DirtyClone | 2026-06-26 | 8.8 High | Yes | No |
⚠ Correction (2026-07-02) — read this if you applied an earlier revision. The user-namespace workaround has been fixed. Earlier revisions set
user.max_user_namespaces=0, which is a global ucount cap that also denies user namespaces to root, silently breaking every root systemd service that uses namespace sandboxing (Prometheus, Grafana, anyDynamicUser=unit). It surfaces as a misleadingNo space left on device/status=217/USERon a host with terabytes free. The workaround now restricts only unprivileged user namespaces, viakernel.unprivileged_userns_clone=0(or the AppArmor restrict knob) — the same reachability path closed, root services untouched. Hosts hardened with the earlier version self-heal on the nextharden_all.shrun (the updatedharden_nftables.shun-caps the live value and restarts wedged services). See the FAQ entry "My monitoring broke after hardening."
Summary
Seven Linux kernel local privilege escalation vulnerabilities have been publicly disclosed in rapid succession, each allowing an unprivileged local user to gain root access. Public proof-of-concept exploits exist for all seven. All should be treated as actively exploitable.
They fall into three overlapping groups by mechanism. The first three (Fragnesia, Dirty Frag, Copy-Fail) corrupt the page cache via a network-stack bug, requiring no special capabilities. ssh-keysign-pwn is a ptrace/pidfd_getfd race during process exit that steals file descriptors from dying privileged processes. The namespace-reachable CVEs (nf_tables Typo, CIFSwitch, DirtyClone) are reached through unprivileged user namespaces and are mitigated by a single unprivileged-userns restriction. DirtyClone is a hybrid: it shares the page-cache corruption technique of the first group and the unprivileged-user-namespace requirement of the third, making it the highest-severity entry at CVSS 8.8.
CVE-2026-31431 (Copy-Fail) has been added to the CISA Known Exploited Vulnerabilities catalog with a mandatory remediation deadline of 2026-05-15 for US federal civilian agencies.
Workarounds are available and effective for all seven. Patched kernels are available for all supported Debian releases.
Am I Affected?
Yes, if all of the following are true:
- You are running Linux.
- Your kernel predates the patched versions for your distribution (see Priority 1 below).
- An untrusted local user can log in or execute code on the system.
Cloud VMs, shared hosting environments, container hosts with untrusted workloads, and developer machines with shared access are all in scope. Systems where only trusted administrators have shell access are lower risk but should still be patched.
The namespace-reachable CVEs (nf_tables Typo, CIFSwitch, DirtyClone) additionally require unprivileged user namespaces to be enabled — the default on most distributions. CIFSwitch further requires cifs-utils to be installed. DirtyClone also requires the esp4/esp6 modules to set up a loopback IPsec tunnel.
Vulnerability Details
CVE-2026-46300 — Fragnesia
Disclosed: 2026-05-13 | CVSS: 7.8 (High) | Subsystem: XFRM ESP-in-TCP (espintcp ULP)
When a TCP socket switches to espintcp upper-layer-protocol mode after file data has already been spliced into its receive queue, the kernel mistakenly treats those queued file-backed pages as ESP ciphertext. This causes one byte of an AES-GCM keystream to be XORed into a read-only file's kernel page-cache entry.
By constructing a lookup table of IV nonces that produce each possible keystream byte, an attacker can flip any byte in a cached file to any value — one byte per invocation. The exploit uses this to overwrite the first 192 bytes of /usr/bin/su in the page cache with a stub that calls setresuid(0,0,0) and launches a shell.
The on-disk binary is never modified. Only the in-memory page cache is affected. A compromised binary persists in RAM and produces a root shell on every invocation until the page cache is explicitly flushed or the system is rebooted.
Discovered by: William Bowling, V12 Security Team · PoC: https://github.com/v12-security/pocs/tree/main/fragnesia · Reference: https://cybersecuritynews.com/fragnesia-linux-vulnerability/
CVE-2026-43284 — Dirty Frag
Disclosed: 2026-05-13 | CVSS: 7.8 (High) | Subsystem: XFRM fragment coalescing
A related logic flaw in how the kernel coalesces socket-buffer fragments causes it to "forget" that a fragment is shared, corrupting memory it was not supposed to touch. The attack surface and exploitation technique are similar to Fragnesia, and the same modules are involved.
Reference: https://cybersecuritynews.com/dirty-frag-linux-vulnerability/
CVE-2026-31431 — Copy-Fail
Disclosed: 2026-04-29 | CVSS: 7.8 (High) | CISA KEV: Yes | Subsystem: AF_ALG kernel crypto API (algif_aead)
A logic flaw in the AEAD crypto-socket path allows page-cache corruption via the AF_ALG socket family (Family 38). The algif_aead module is the direct attack surface; af_alg is its parent and must also be disabled.
Reference: https://cybersecuritynews.com/linux-kernel-0-day-vulnerability-exploited/
CVE-2026-46333 — ssh-keysign-pwn
Disclosed: 2026-05-15 | CVSS: 7.8 (High) | Subsystem: ptrace / pidfd_getfd — process-exit race__ptrace_may_access() skips its dumpable check when the target task's mm is NULL. During do_exit(), the kernel runs exit_mm() before exit_files(), creating a window where a privileged process has dropped its memory mappings but still holds its open file descriptors. An unprivileged process running under the same UID can call pidfd_getfd(2) in that window and steal open file descriptors from the dying process.
If those descriptors point at root-owned files opened before privilege drop — such as SSH host keys opened by ssh-keysign before permanently_set_uid(), or /etc/shadow opened by chage before setreuid() — the attacker now holds a read handle on those files.
Unlike the page-cache CVEs, this vulnerability does not corrupt in-memory binaries. It is a file-descriptor theft primitive.
Discovered by: Qualys · PoC: https://github.com/0xdeadbeefnetwork/ssh-keysign-pwn · Upstream fix: https://github.com/torvalds/linux/commit/31e62c2ebbfdc3fe3dbdf5e02c92a9dc67087a3a · Reference: https://almalinux.org/blog/2026-05-15-ssh-keysign-pwn-cve-2026-46333/
CVE-2026-23111 — nf_tables Typo
Disclosed: 2026-06-08 (PoC) | CVSS: 7.8 (High) | Upstream patch: 2026-02-05 | Subsystem: nf_tables (Netfilter packet filtering)
A single inverted check in nft_map_catchall_activate() causes the kernel to use the wrong sense of an activity check while aborting a failed nf_tables transaction. Each aborted transaction permanently decrements a chain's use counter; once it reaches zero, the chain is freed while a stale reference still points at it, producing a use-after-free. A fully weaponized public exploit achieves root with greater than 99% reliability across Debian Bookworm and Trixie.
nf_tables cannot be disabled on a ggRock host — it backs the firewall. The practical mitigation is denying unprivileged user-namespace creation, which closes the exploit's reachability path (the attacker needs CAP_NET_ADMIN from a userns) without touching the firewall stack.
Upstream fix: https://github.com/torvalds/linux/commit/3da1fdf4efbc490041eb4f836bf596201203f8f2 · Reference: https://thehackernews.com/2026/06/one-character-linux-kernel-flaw-enables.html
CVE-2026-46243 — CIFSwitch
Disclosed: 2026-05-27 | CVSS: 7.8 (High) | CVE assigned: 2026-06-01 | Subsystem: CIFS client cifs.spnego upcall + cifs-utils helper
The kernel's cifs.spnego key type did not validate the origin of its key description. An unprivileged user could call request_key("cifs.spnego", fake_description, ...) directly, causing the default request-key rule to launch cifs.upcall as root. A forged description containing an attacker-controlled pid makes the root helper switch into the attacker's namespace, where a namespace-local nsswitch.conf can run attacker code as root — for example, dropping a sudoers.d entry.
On a diskless ggRock boot host, cifs-utils is typically not installed — if cifs.upcall is absent, the host is not exposed. The same unprivileged-user-namespace restriction that mitigates the nf_tables Typo also closes the CIFSwitch reachability path.
Discovered by: Asim Manizada · PoC: https://github.com/manizada/CIFSwitch · Reference: https://heyitsas.im/posts/cifswitch/
CVE-2026-43503 — DirtyClone
Disclosed: 2026-06-26 | CVSS: 8.8 (High — highest in this set) | Patch merged: 2026-05-21 | Subsystem: Networking packet-clone helpers (__pskb_copy_fclone(), skb_shift())
DirtyClone is the fourth variant in the DirtyFrag family and the highest-severity entry here. When the kernel clones a network packet internally, __pskb_copy_fclone() and skb_shift() drop the SKBFL_ZEROCOPY_FRAG flag that marks packet memory as shared with a file on disk. The attacker loads a privileged binary such as /usr/bin/su into the page cache, wires those pages into a network packet, forces a kernel clone, then routes the clone through a loopback IPsec tunnel they control. The AES-GCM decryption step overwrites the binary's login checks in the page cache. The next invocation of su hands over root.
The on-disk binary is never modified; a reboot restores it. File-integrity tools will not detect the attack, and it leaves no audit trail.
The exploit requires CAP_NET_ADMIN to configure the loopback IPsec tunnel. On Debian and Fedora, an unprivileged user obtains this capability by creating a user namespace — the default configuration. Because the page cache is shared at the host level, modifications made inside a namespace affect every process on the machine, which is why CVSS is 8.8 rather than 7.8.
Unlike the other page-cache CVEs in this set, the modification is host-wide — it affects every process on the machine, not just those in the attacker's namespace. Reboot after flushing the cache to fully restore affected binaries.
If harden_all.sh has already run harden_fragnesia.sh and harden_nftables.sh, both DirtyClone gates are already closed. The dedicated script applies and verifies them independently.
Discovered by: JFrog Security Research (exploit walkthrough), Hyunwoo Kim (multi-site patch) · PoC: https://research.jfrog.com/post/dissecting-and-exploiting-linux-lpe-variant-dirtyclone-cve-2026-43503/ · Upstream fix: commit 48f6a5356a33 — https://github.com/torvalds/linux/commit/48f6a5356a33 · Reference: https://thehackernews.com/2026/06/new-dirtyclone-linux-kernel-flaw-lets.html
Impact
CVE | Codename | Privilege Required | Race Condition | Root Shell |
|---|---|---|---|---|
CVE-2026-46300 | Fragnesia | Unprivileged local user | No | Yes |
CVE-2026-43284 | Dirty Frag | Unprivileged local user | No | Yes |
CVE-2026-31431 | Copy-Fail | Unprivileged local user | No | Yes |
CVE-2026-46333 | ssh-keysign-pwn | Unprivileged local user | Soft (exit window) | Yes — via host key / shadow read |
CVE-2026-23111 | nf_tables Typo | Unprivileged local user + userns | No | Yes — also container escape |
CVE-2026-46243 | CIFSwitch | Unprivileged local user + userns | No | Yes — via forged upcall + NSS |
CVE-2026-43503 | DirtyClone | Unprivileged local user + userns | No | Yes — host-wide page cache, CVSS 8.8 |
Recommended Actions
Priority 1 — Apply the Upstream Patch (Permanent Fix)
Patched kernels are available for all supported Debian releases. The minimum patched versions are:
Debian Release | Minimum Patched Kernel |
|---|---|
Debian 13 Trixie | 6.12.85-1 |
Debian 12 Bookworm | 6.1.170-1 |
Debian 11 Bullseye | 5.10.251-3 |
Update and reboot: |
apt update && apt full-upgrade && reboot
Verify your running kernel version after reboot:
uname -r
The patched kernel is available via the standard [security] repository, enabled by default on all supported Debian releases. On Debian 13 Trixie, sources configuration has moved from /etc/apt/sources.list to /etc/apt/sources.list.d/debian.sources — the security repository is included in the default debian.sources file.
The minimum patched versions above were validated against Copy-Fail (CVE-2026-31431). The DirtyClone fix (commit 48f6a5356a33) and the nf_tables/CIFSwitch fixes landed on different dates. Confirm patch status for those CVEs against the Debian security-tracker links in References if you need version-exact assurance.
Priority 2 — Apply Workaround (If Immediate Patching Is Not Possible)
Each CVE has a dedicated hardening script. The simplest approach is the master script, which fetches and runs all seven in the correct order:
wget -O - https://images.ggleap.com/ManagedServices/Scripts/harden_all.sh | bash -
This mitigation disables IPsec (ESP/RXRPC) for CVE-2026-46300, CVE-2026-43284, and CVE-2026-43503; AF_ALG crypto sockets for CVE-2026-31431; unrestricted ptrace attach for CVE-2026-46333; and unprivileged user namespaces for CVE-2026-23111, CVE-2026-46243, and CVE-2026-43503. Restricting unprivileged user namespaces also breaks rootless containers (Podman, rootless Docker). Review the Caveats section before applying.
The user-namespace gate uses
kernel.unprivileged_userns_clone=0, notuser.max_user_namespaces=0. The former blocks only unprivileged callers; root systemd service sandboxes keep working. The latter is a global ucount cap that also denies root and breaks Prometheus/Grafana and anyDynamicUser=service (surfacing as a bogusNo space left on device).harden_nftables.showns this gate;harden_cifswitch.shandharden_dirtyclone.shdefer to it.
The individual scripts are listed below for reference. The authoritative, full self-healing versions are hosted at https://images.ggleap.com/ManagedServices/Scripts/.
harden_fragnesia.sh — CVE-2026-46300
#!/bin/bash
# Fragnesia (CVE-2026-46300) Hardening Script
# Ref: https://cybersecuritynews.com/fragnesia-linux-vulnerability/
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
echo "--- Fragnesia (CVE-2026-46300) Hardening ---"
MODULES_IN_USE=false
if lsmod | grep -qE "^(esp4|esp6|rxrpc)"; then
echo "[!] WARNING: ESP/RXRPC modules currently loaded."
ip xfrm state 2>/dev/null | grep -q "proto esp" && \
echo "[!] Active IPsec SA detected — unloading will drop tunnels."
MODULES_IN_USE=true
fi
CONF_FILE="/etc/modprobe.d/disable-fragnesia.conf"
cat <<EOF > "$CONF_FILE"
# Fragnesia (CVE-2026-46300) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
EOF
echo "[+] Blacklist written."
if [ "$MODULES_IN_USE" = true ]; then
echo "[!] Skipping module unload — active sessions detected."
else
modprobe -r esp4 esp6 rxrpc 2>/dev/null
lsmod | grep -qE "^(esp4|esp6|rxrpc)" && echo "[!] Unload failed — reboot required." || echo "[+] Modules unloaded."
fi
echo "[*] Flushing page cache..."
sync; echo 1 > /proc/sys/vm/drop_caches
command -v update-initramfs &>/dev/null && update-initramfs -u
python3 -c "import socket; socket.socket(33, 2, 0)" 2>/dev/null \
&& echo "[!] FAIL: socket still created — reboot required." \
|| echo "[+] VERIFIED: kernel rejects vulnerable socket type."
harden_net_frag.sh — CVE-2026-43284
#!/bin/bash
# Dirty Frag (CVE-2026-43284) Mitigation Script
# Ref: https://cybersecuritynews.com/dirty-frag-linux-vulnerability/
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
set -e
CONFIG_FILE="/etc/modprobe.d/dirtyfrag.conf"
VULN_MODULES=("esp4" "esp6" "rxrpc")
echo "--- Dirty Frag (CVE-2026-43284) Mitigation ---"
cat <<EOF > "${CONFIG_FILE}.tmp"
# Dirty Frag (CVE-2026-43284) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
EOF
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
echo "[+] Blacklist written to $CONFIG_FILE"
for mod in "${VULN_MODULES[@]}"; do
lsmod | grep -q "^$mod " && { modprobe -r "$mod" 2>/dev/null && echo "[+] $mod unloaded." || echo "[!] $mod unload failed."; } || echo "[*] $mod not loaded."
done
sync; echo 1 > /proc/sys/vm/drop_caches
command -v update-initramfs &>/dev/null && update-initramfs -u
SOCK_ERR=$(python3 -c "import socket; socket.socket(33, 2, 0)" 2>&1) || true
if ! python3 -c "import socket; socket.socket(33, 2, 0)" 2>/dev/null; then
echo "[+] VERIFIED: kernel rejects vulnerable socket type."
else
echo "[!] FAIL: socket still created — reboot required."
fi
harden_crypto.sh — CVE-2026-31431
#!/bin/bash
# Copy-Fail (CVE-2026-31431) Hardening Script
# Mitigates AF_ALG/algif_aead privilege escalation via module blacklist
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
echo "--- Copy-Fail (CVE-2026-31431) Hardening ---"
CONF_FILE="/etc/modprobe.d/disable-copy-fail.conf"
cat <<EOF > "$CONF_FILE"
# Copy-Fail (CVE-2026-31431) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
install algif_aead /bin/false
install af_alg /bin/false
alias net-pf-38 off
EOF
echo "[+] Blacklist written to $CONF_FILE"
modprobe -r algif_aead 2>/dev/null
modprobe -r af_alg 2>/dev/null
lsmod | grep -qE "^(algif_aead|af_alg)" && echo "[!] Unload failed — reboot required." || echo "[+] Modules unloaded."
command -v update-initramfs &>/dev/null && update-initramfs -u
python3 -c "import socket; socket.socket(38, 5, 0)" 2>/dev/null \
&& echo "[!] FAIL: socket still created — reboot required." \
|| echo "[+] VERIFIED: kernel rejects AF_ALG socket."
harden_sshkeysignpwn.sh — CVE-2026-46333
#!/bin/bash
# ssh-keysign-pwn (CVE-2026-46333) Hardening Script
# Ref: https://almalinux.org/blog/2026-05-15-ssh-keysign-pwn-cve-2026-46333/
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
echo "--- ssh-keysign-pwn (CVE-2026-46333) Hardening ---"
SYSCTL_CONF="/etc/sysctl.d/99-ssh-keysign-pwn.conf"
PTRACE_SCOPE_FILE="/proc/sys/kernel/yama/ptrace_scope"
[ ! -f "$PTRACE_SCOPE_FILE" ] && echo "[!] Yama LSM not loaded." && exit 1
PTRACE_PIDS=$(awk '/TracerPid:/ && $2 != "0" {c++} END {print c+0}' /proc/[0-9]*/status 2>/dev/null)
[ "$PTRACE_PIDS" -gt 0 ] && echo "[!] WARNING: $PTRACE_PIDS process(es) being ptraced."
sysctl -w kernel.yama.ptrace_scope=3 || { echo "[!] sysctl write failed."; exit 1; }
cat > "$SYSCTL_CONF" <<EOF
# ssh-keysign-pwn (CVE-2026-46333) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
# Scope 3: no process may ptrace any other. Use 2 if admins need debuggers.
kernel.yama.ptrace_scope = 3
EOF
echo "[+] Persistent config written to $SYSCTL_CONF"
ACTIVE_SCOPE=$(cat "$PTRACE_SCOPE_FILE")
[ "$ACTIVE_SCOPE" -ge 2 ] \
&& echo "[+] VERIFIED: ptrace_scope=$ACTIVE_SCOPE — exploit paths blocked." \
|| { echo "[!] FAIL: ptrace_scope=$ACTIVE_SCOPE."; exit 1; }
harden_nftables.sh — CVE-2026-23111 (v3 — corrected)
Restricts unprivileged user namespaces via the privilege-selective knob. Does not set user.max_user_namespaces=0. Self-heals hosts poisoned by earlier versions (un-caps the live value, restarts wedged monitoring). Sole owner of the userns gate.
#!/bin/bash
# nf_tables Typo (CVE-2026-23111) Hardening Script -- v3
# Restricts UNPRIVILEGED user namespaces only. Does NOT set user.max_user_namespaces=0
# (that global cap also breaks ROOT systemd sandboxes -- Prometheus/Grafana). Self-heals
# hosts poisoned by earlier versions.
# NOTE: the same gate also blocks CIFSwitch (CVE-2026-46243) and DirtyClone (CVE-2026-43503).
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
echo "--- nf_tables Typo (CVE-2026-23111) Hardening ---"
SYSCTL_CONF="/etc/sysctl.d/99-nftables-userns.conf"
CLONE_KNOB="/proc/sys/kernel/unprivileged_userns_clone"
AA_KNOB="/proc/sys/kernel/apparmor_restrict_unprivileged_userns"
# Detect the privilege-selective knob (Debian: clone; newer kernels: AppArmor)
if [ -f "$CLONE_KNOB" ]; then GATE=clone
elif [ -f "$AA_KNOB" ]; then GATE=apparmor
else GATE=none; fi
# a live max of 0 means an earlier version poisoned this host
PRE_MAX=$(cat /proc/sys/user/max_user_namespaces 2>/dev/null || echo unavailable)
# persistent config -- NO user.max_user_namespaces line
{
echo "# nf_tables Typo (CVE-2026-23111) mitigation -- $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "# Blocks UNPRIVILEGED userns only; root systemd sandboxes unaffected."
echo "# DO NOT add user.max_user_namespaces=0 (breaks Prometheus/Grafana/DynamicUser=)."
case "$GATE" in
clone) echo "kernel.unprivileged_userns_clone = 0" ;;
apparmor) echo "kernel.apparmor_restrict_unprivileged_userns = 1" ;;
none) echo "# no selective knob on this kernel -- deploy the patched kernel instead" ;;
esac
} > "$SYSCTL_CONF"
sysctl --system >/dev/null 2>&1 || true
# self-heal: un-cap the live value if a legacy run left it at 0 (sysctl --system won't revert it)
if [ "$(cat /proc/sys/user/max_user_namespaces 2>/dev/null)" = "0" ] && [ "$GATE" != none ]; then
sysctl -w user.max_user_namespaces=15000 >/dev/null
fi
case "$GATE" in
clone) sysctl -w kernel.unprivileged_userns_clone=0 >/dev/null ;;
apparmor) sysctl -w kernel.apparmor_restrict_unprivileged_userns=1 >/dev/null ;;
none) echo "[!] No selective userns knob -- refusing the max=0 sledgehammer; deploy the patched kernel." ;;
esac
# self-heal: restart monitoring units the legacy cap wedged
if [ "$PRE_MAX" = "0" ]; then
for svc in prometheus.service grafana-server.service; do
systemctl cat "$svc" &>/dev/null || continue
systemctl is-active --quiet "$svc" || { systemctl reset-failed "$svc" 2>/dev/null; systemctl start "$svc" 2>/dev/null; }
done
fi
# verify: unprivileged userns creation must fail; root cap must stay nonzero
if ! runuser -u nobody -- unshare -U true 2>/dev/null; then
echo "[+] VERIFIED: unprivileged user namespace creation blocked."
else
echo "[!] unprivileged userns still creatable ($GATE) -- deploy the patched kernel."
fi
echo "[*] user.max_user_namespaces = $(cat /proc/sys/user/max_user_namespaces 2>/dev/null) (nonzero = root sandboxes OK)"
harden_cifswitch.sh — CVE-2026-46243
#!/bin/bash
# CIFSwitch (CVE-2026-46243) Mitigation Script
# Advisory: https://heyitsas.im/posts/cifswitch/
# Userns gate is owned by harden_nftables.sh (this script does request-key + blacklist only).
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
set -e
REQKEY_RULE="/etc/request-key.d/cifs.spnego.conf"
CIFS_BLACKLIST="/etc/modprobe.d/cifswitch.conf"
KEYCTL=$(command -v keyctl 2>/dev/null || echo "/usr/bin/keyctl")
echo "--- CIFSwitch (CVE-2026-46243) Mitigation ---"
command -v cifs.upcall &>/dev/null || echo "[+] cifs.upcall not found — cifs-utils not installed (primary mitigation)."
[ -f "$REQKEY_RULE" ] && [ ! -f "${REQKEY_RULE}.cifswitch.bak" ] && cp "$REQKEY_RULE" "${REQKEY_RULE}.cifswitch.bak"
cat > "$REQKEY_RULE" <<EOF
# CIFSwitch (CVE-2026-46243) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
create cifs.spnego * * $KEYCTL negate %k 30 %S
EOF
echo "[+] Safe negate rule written to $REQKEY_RULE"
cat > "$CIFS_BLACKLIST" <<EOF
# CIFSwitch (CVE-2026-46243) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
install cifs /bin/false
blacklist cifs
EOF
echo "[+] Module blacklist written to $CIFS_BLACKLIST"
if lsmod | grep -q '^cifs '; then
grep -q ' cifs ' /proc/mounts 2>/dev/null \
&& echo "[!] Active CIFS mounts — skipping unload." \
|| { modprobe -r cifs 2>/dev/null && echo "[+] cifs unloaded." || echo "[!] cifs unload failed — reboot to complete."; }
fi
command -v update-initramfs &>/dev/null && update-initramfs -u
harden_dirtyclone.sh — CVE-2026-43503 (v2 — corrected)
esp blacklist + host-wide page-cache flush. The unprivileged-userns gate is report-only here — owned by harden_nftables.sh. This version no longer writes user.max_user_namespaces=0 (the removed second writer).
#!/bin/bash
# DirtyClone (CVE-2026-43503) Hardening Script -- v2
# CVSS 8.8. esp blacklist + page-cache flush. Unprivileged-userns gate is owned by
# harden_nftables.sh (report-only here). No longer writes user.max_user_namespaces=0.
# Patch: commit 48f6a5356a33, merged 2026-05-21
if [ "$EUID" -ne 0 ]; then echo "Please run as root"; exit 1; fi
echo "--- DirtyClone (CVE-2026-43503) Hardening ---"
CONF_FILE="/etc/modprobe.d/dirtyclone.conf"
# module blacklist (independent DirtyClone layer)
cat > "$CONF_FILE" <<EOF
# DirtyClone (CVE-2026-43503) mitigation — $(date -u +%Y-%m-%dT%H:%M:%SZ)
# Independent layer: removal of fragnesia/dirtyfrag blacklists won't reopen this CVE.
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
EOF
echo "[+] Blacklist written to $CONF_FILE"
MODULES_IN_USE=false
lsmod | grep -qE "^(esp4|esp6|rxrpc)" && MODULES_IN_USE=true
if [ "$MODULES_IN_USE" = true ]; then
ip xfrm state 2>/dev/null | grep -q "proto esp" && echo "[!] Active IPsec SA detected."
echo "[!] Skipping module unload — active sessions. Schedule a reboot."
else
modprobe -r esp4 esp6 rxrpc 2>/dev/null
lsmod | grep -qE "^(esp4|esp6|rxrpc)" && echo "[!] Unload failed — reboot required." || echo "[+] Modules unloaded."
fi
# userns gate: REPORT ONLY -- owned by harden_nftables.sh (no userns sysctl written here)
if runuser -u nobody -- unshare -U true 2>/dev/null; then
echo "[!] Unprivileged userns NOT blocked -- run harden_nftables.sh to close the CAP_NET_ADMIN pivot."
else
echo "[+] Unprivileged userns blocked -- CAP_NET_ADMIN gate closed."
fi
# page cache is corrupted host-wide -- flush, then reboot if exploitation is suspected
echo "[*] Flushing page cache (host-wide)..."
sync; echo 1 > /proc/sys/vm/drop_caches
echo "[+] Page cache flushed."
command -v update-initramfs &>/dev/null && update-initramfs -u
# verify (AF_RXRPC uses protocol 2 here)
if ! python3 -c "import socket; socket.socket(33, 2, 2)" 2>/dev/null; then
echo "[+] VERIFIED: kernel rejects AF_RXRPC socket (IPsec module path blocked)."
else
echo "[!] FAIL: AF_RXRPC socket still creatable — reboot may be required."
fi
echo ""
echo "--- Summary ---"
echo "Module blacklist: $CONF_FILE"
echo "ESP/RXRPC loaded: $(lsmod | grep -cE '^(esp4|esp6|rxrpc) ' || true) (0 = good)"
echo "Cache flush: done"
echo "Unpriv userns gate: owned by harden_nftables.sh"
echo "Upstream fix: commit 48f6a5356a33 (merged 2026-05-21)"
Priority 3 — Incident Response (If Exploitation Is Suspected)
For page-cache CVEs: do not invoke /usr/bin/su until the page cache has been cleared — doing so may spawn a root shell for any user. For ssh-keysign-pwn: rotate SSH host keys and audit /etc/shadow immediately. For CIFSwitch: audit /etc/sudoers.d for unexpected entries. For DirtyClone: the modification is host-wide — reboot immediately after flushing the cache.
Page-cache CVEs (CVE-2026-46300, CVE-2026-43284, CVE-2026-31431, CVE-2026-43503):
# Flush the page cache immediately
sync && echo 1 > /proc/sys/vm/drop_caches
# Reboot as soon as operationally possible, then review:
grep -E "su|sudo|root" /var/log/auth.log
journalctl _COMM=su --since "24 hours ago"
Check for unexpected root-owned processes or modified sudoers/passwd entries.
ssh-keysign-pwn (CVE-2026-46333):
# Rotate all SSH host keys
rm /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub
ssh-keygen -A
systemctl restart sshd
Audit /etc/shadow for unexpected password-hash changes and review auth logs.
nf_tables Typo and CIFSwitch (CVE-2026-23111, CVE-2026-46243):
- Audit
/etc/sudoers.d/and/etc/passwdfor unexpected entries. - Check for unexpected
nsswitch.conforlibnss_*.sofiles in user-writable paths. - Review logs for unprivileged
unshare/nftactivity.
Apply the workaround or patch before bringing the system back into service.
Caveats
Fragnesia / Dirty Frag / DirtyClone (CVE-2026-46300, CVE-2026-43284, CVE-2026-43503)
Blacklisting esp4 and esp6 will break kernel-mode IPsec. This affects site-to-site VPN tunnels using strongSwan or Libreswan in kernel ESP mode, and any IKEv1/IKEv2 configuration that offloads ESP processing to the kernel. Userspace VPN implementations (WireGuard, OpenVPN, Tailscale) are not affected. If you run a VPN gateway or any host with active IPsec SAs, review your topology and coordinate a maintenance window first. The scripts detect active IPsec SAs and skip the module unload, but the blacklist is still written — a subsequent reboot drops those tunnels.
Copy-Fail (CVE-2026-31431)
Blacklisting af_alg removes AF_ALG socket access. This affects some hardware crypto-offload paths, cryptsetup benchmark and similar kernel-crypto benchmarking tools, and certain OpenSSL engine configurations that use AF_ALG explicitly. Standard software-path TLS, LUKS/dm-crypt disk encryption, and most application crypto are unaffected.
ssh-keysign-pwn (CVE-2026-46333)
Setting ptrace_scope=3 disables all ptrace attach on the system — breaking gdb attaching to a running process, strace -p <pid>, and any tooling that uses PTRACE_ATTACH or pidfd_getfd on a live process. Use ptrace_scope=2 instead if administrator-level debugger access is needed; both values block all known public PoCs. ptrace_scope is a runtime change — it takes effect immediately without a reboot and does not interrupt any ptrace sessions already in progress.
nf_tables Typo / CIFSwitch / DirtyClone (CVE-2026-23111, CVE-2026-46243, CVE-2026-43503)
Restricting unprivileged user namespaces (via kernel.unprivileged_userns_clone=0, or kernel.apparmor_restrict_unprivileged_userns=1 on kernels that lack the clone knob) disables unprivileged userns creation. This affects rootless containers (Podman, rootless Docker, unprivileged LXC) and sandboxing tools that rely on user namespaces (Flatpak, Bubblewrap, some browser sandboxes). The firewall stack itself is unaffected — nf_tables continues to function normally. One sysctl covers three CVEs.
Do NOT use
user.max_user_namespaces=0. It is a global ucount cap that also blocks root — including systemd's own service sandboxing (ProtectSystem=,PrivateTmp=,DynamicUser=, etc.). Root services that need a namespace then fail withstatus=217/USERandNo space left on device(the kernel returnsENOSPCwhen the userns count cap is hit; systemd relays the raw errno). On ggRock hosts this takes down Prometheus and Grafana. The privilege-selective knob closes the exact same exploit path without this collateral. On a kernel with neither selective knob, do not fall back to the cap — deploy the patched kernel (Priority 1) instead.
CIFSwitch additionally overrides the cifs.spnego request-key rule with a safe negate and blacklists the cifs module. On diskless ggRock boot hosts these are typically no-ops since cifs-utils is usually not installed.
Verification
After applying the workaround, confirm the following on each system:
# No vulnerable modules should be loaded
lsmod | grep -E "esp4|esp6|rxrpc|algif_aead|af_alg|cifs"
# Socket creation should be rejected (Fragnesia / Dirty Frag)
python3 -c "import socket; socket.socket(33, 2, 0)"
# Socket creation should be rejected (Copy-Fail)
python3 -c "import socket; socket.socket(38, 5, 0)"
# Socket creation should be rejected (DirtyClone — note protocol 2, not 0)
python3 -c "import socket; socket.socket(33, 2, 2)"
# ptrace_scope should be 2 or 3
cat /proc/sys/kernel/yama/ptrace_scope
# Unprivileged user namespaces should be blocked (non-zero exit)
runuser -u nobody -- unshare -U true; echo "exit: $?"
# The privilege-selective knob should be 0 (Debian clone knob)
cat /proc/sys/kernel/unprivileged_userns_clone
# CRITICAL: the global cap must remain NONZERO. A 0 here means root systemd
# sandboxes (Prometheus/Grafana, DynamicUser=) are broken — the old bug.
cat /proc/sys/user/max_user_namespaces
# cifs.spnego rule should reference keyctl negate, not cifs.upcall
cat /etc/request-key.d/cifs.spnego.conf
All Python socket commands above should raise OSError: [Errno 97] Address family not supported by protocol — once the relevant module is blacklisted, the address family is unavailable. A clean exit (return code 0) means the module mitigation has not taken effect and a reboot is required. ptrace_scope should be ≥ 2. The unshare -U test should return a non-zero exit. kernel.unprivileged_userns_clone should be 0 while user.max_user_namespaces remains nonzero. The cifs.spnego rule should reference keyctl negate.
Frequently Asked Questions
Does this affect containers? Containers share the host kernel. If the host kernel is vulnerable and a container runs with sufficient privileges to open the relevant socket families, it is in scope. Rootless containers and those with restricted seccomp/AppArmor profiles that block socket(AF_RXRPC, ...) and socket(AF_ALG, ...) have a reduced attack surface. CVE-2026-46333, CVE-2026-23111, CVE-2026-46243, and CVE-2026-43503 all carry container-escape potential. DirtyClone is particularly notable: page-cache modifications made inside a namespace affect every process on the host machine.
Does this affect read-only root filesystems? For the page-cache CVEs (including DirtyClone), the exploit targets the page cache, not the filesystem — a read-only root filesystem does not prevent the attack. For CVE-2026-46333, a read-only filesystem is irrelevant; the exploit reads in-memory file descriptors, not on-disk files. CVE-2026-46243 writes to sudoers.d via NSS inside the root helper, so a writable /etc is part of its chain.
Do I need to apply all seven scripts? If you are patching promptly, one patched kernel covers all seven. If you are applying workarounds, run all seven (the master harden_all.sh does this in the correct order). harden_nftables.sh closes the unprivileged user-namespace gate relied upon by CIFSwitch and DirtyClone, and harden_fragnesia.sh blacklists the esp4/esp6 modules relied upon by DirtyClone — so ordering matters and the master script handles it correctly.
My monitoring broke after hardening — Grafana shows "No data" and Prometheus won't start. Why? An earlier revision of harden_nftables.sh / harden_dirtyclone.sh set user.max_user_namespaces=0, a global cap that also denies user namespaces to root. Systemd can't build the sandbox for services like Prometheus/Grafana, so they fail with status=217/USER and prometheus.service: Failed to set up user namespacing: No space left on device — despite the disk being fine (zpool/zfs list show ample free space). Grafana then reads "No data" because its Prometheus datasource is down. Fix: re-run the current harden_all.sh (or just harden_nftables.sh). The updated script detects the poison, un-caps the live value, restarts the wedged units, and reapplies the correct unprivileged-only restriction. Fleet triage — any node printing 0 is still affected:
sysctl -n user.max_user_namespaces # 0 == monitoring stack down; re-run harden_nftables.sh
Can the workaround be reversed after patching? Yes. After confirming you are running a patched kernel (see Priority 1), remove the config files and revert the sysctls:
rm /etc/modprobe.d/disable-fragnesia.conf
rm /etc/modprobe.d/dirtyfrag.conf
rm /etc/modprobe.d/disable-copy-fail.conf
rm /etc/modprobe.d/cifswitch.conf
rm /etc/modprobe.d/dirtyclone.conf
rm /etc/sysctl.d/99-ssh-keysign-pwn.conf
rm /etc/sysctl.d/99-nftables-userns.conf
# Restore the original cifs.spnego rule if Kerberos CIFS is needed:
[ -f /etc/request-key.d/cifs.spnego.conf.cifswitch.bak ] && \
mv /etc/request-key.d/cifs.spnego.conf.cifswitch.bak /etc/request-key.d/cifs.spnego.conf
update-initramfs -u
sysctl -w kernel.yama.ptrace_scope=1 # Debian default
sysctl -w kernel.unprivileged_userns_clone=1 # re-enable unprivileged userns (Debian default)
reboot
Note: no user.max_user_namespaces reset is needed — the corrected workaround never sets it. If a host was previously hardened with the old version, running the current harden_nftables.sh already restored it; on reboot the removed drop-in leaves the kernel default in place.
Revision History
- 2026-07-02 — Corrected the user-namespace workaround. Replaced
user.max_user_namespaces=0(a global ucount cap that also disabled root systemd service sandboxing and broke Prometheus/Grafana andDynamicUser=services, surfacing as a misleadingNo space left on device/status=217/USER) with an unprivileged-only restriction viakernel.unprivileged_userns_clone=0(AppArmorapparmor_restrict_unprivileged_userns=1fallback). Madeharden_nftables.sh(v3) the sole owner of the gate and self-healing for hosts poisoned by the earlier version; removed the duplicate userns writer fromharden_dirtyclone.sh(v2). Updated Caveats, Verification, and Reversal accordingly; added the "monitoring broke after hardening" FAQ. - 2026-06-27 — Added DirtyClone (CVE-2026-43503); expanded namespace-gate coverage to three CVEs.
- Earlier — Fragnesia, Dirty Frag, Copy-Fail, ssh-keysign-pwn, nf_tables Typo, CIFSwitch.
References
- CVE-2026-46300 Advisory: https://cybersecuritynews.com/fragnesia-linux-vulnerability/
- CVE-2026-43284 Advisory: https://cybersecuritynews.com/dirty-frag-linux-vulnerability/
- CVE-2026-31431 Advisory: https://cybersecuritynews.com/linux-kernel-0-day-vulnerability-exploited/
- CVE-2026-46333 Advisory: https://almalinux.org/blog/2026-05-15-ssh-keysign-pwn-cve-2026-46333/
- CVE-2026-23111 Advisory: https://thehackernews.com/2026/06/one-character-linux-kernel-flaw-enables.html
- CVE-2026-46243 Advisory: https://heyitsas.im/posts/cifswitch/
- CVE-2026-43503 Advisory: https://thehackernews.com/2026/06/new-dirtyclone-linux-kernel-flaw-lets.html
- CVE-2026-43503 JFrog walkthrough: https://research.jfrog.com/post/dissecting-and-exploiting-linux-lpe-variant-dirtyclone-cve-2026-43503/
- Fragnesia PoC: https://github.com/v12-security/pocs/tree/main/fragnesia
- ssh-keysign-pwn PoC: https://github.com/0xdeadbeefnetwork/ssh-keysign-pwn
- CIFSwitch PoC: https://github.com/manizada/CIFSwitch
- Debian security tracker — CVE-2026-46300: https://security-tracker.debian.org/tracker/CVE-2026-46300
- Debian security tracker — CVE-2026-43284: https://security-tracker.debian.org/tracker/CVE-2026-43284
- Debian security tracker — CVE-2026-31431: https://security-tracker.debian.org/tracker/CVE-2026-31431
- Debian security tracker — CVE-2026-46333: https://security-tracker.debian.org/tracker/CVE-2026-46333
- Debian security tracker — CVE-2026-23111: https://security-tracker.debian.org/tracker/CVE-2026-23111
- Debian security tracker — CVE-2026-46243: https://security-tracker.debian.org/tracker/CVE-2026-46243
- Debian security tracker — CVE-2026-43503: https://security-tracker.debian.org/tracker/CVE-2026-43503
- CISA KEV catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- Dirty Pipe (related prior art): https://cybersecuritynews.com/8-year-old-linux-kernel-bug/
Updated on: 02/07/2026
Thank you!
