Files
SKELETONKEY/modules/nft_catchall_cve_2026_23111/MODULE.md
T
KaraZajac 4d0a0e2443
release / build (arm64) (push) Has been cancelled
release / build (x86_64) (push) Has been cancelled
release / build (x86_64-static / musl) (push) Has been cancelled
release / build (arm64-static / musl) (push) Has been cancelled
release / release (push) Has been cancelled
modules: add nft_catchall (CVE-2026-23111, nf_tables catch-all abort UAF)
The newest nftables LPE: a use-after-free in the nf_tables transaction-
abort path. An inverted condition (a stray '!') in
nft_map_catchall_activate() makes the abort path process active catch-all
map elements instead of skipping them; a catch-all GOTO element drives a
chain's use-count to zero so a following DELCHAIN frees it while the
catch-all verdict still references it -> UAF, escalatable from an
unprivileged user (userns + nftables) via modprobe_path/selinux_state ROP.
Fixed upstream by f41c5d1; CWE-416, CVSS 7.8; not in CISA KEV. Public
reproduction by FuzzingLabs.

Takes the corpus to 43 modules / 38 CVEs.

🟡 reconstructed trigger, primitive-only, NOT VM-verified — same contract
as nf_tables (CVE-2024-1086). detect() version-gates (catch-all elems
~5.13; Debian backports 6.1.164/6.12.73/6.18.10, 7.0+ inherits) AND
requires unprivileged user_ns clone (else PRECOND_FAIL). exploit() forks
an isolated child, builds a verdict map with a catch-all GOTO element,
provokes an aborting batch to drive the abort-path UAF, observes slabinfo,
and returns EXPLOIT_FAIL — the per-kernel leak + R/W + modprobe_path ROP
is not bundled. kernel_range table verified drift-clean against the live
Debian tracker (the 6.18 branch / 6.18.10 fix is the real forky/sid
backport; the earlier 7.0.10 figure was wrong).

Wired: registry, Makefile, safety rank (35), 6 detect() test rows (version
+ userns gating), CVE_METADATA.json + cve_metadata.c + KEV_CROSSREF.md
(sorted insert, CWE-416/T1068/not-KEV), README + CVES.md + website counts
(43/38) + yellow pill, RELEASE_NOTES v0.9.11, verify-vm target. Credit:
FuzzingLabs + upstream fix f41c5d1. Version 0.9.11.
2026-06-08 17:42:19 -04:00

63 lines
2.6 KiB
Markdown

# nft_catchall — CVE-2026-23111
An nf_tables use-after-free reachable from an unprivileged user: an
inverted condition in `nft_map_catchall_activate()` mishandles catch-all
map elements on transaction abort, freeing a chain that a catch-all GOTO
verdict still references.
## The bug
nftables *maps* can hold a **catch-all** element — a default that matches
when no other element does — and in a verdict map that element carries a
GOTO/JUMP to a chain. `nft_map_catchall_activate()` runs during the
**abort** phase of a netlink transaction to re-activate elements that a
rolled-back batch had touched. A single inverted `!` makes it operate on
*active* catch-all elements instead of skipping them, so the referenced
chain's use-count is driven to zero; a subsequent `DELCHAIN` frees the
chain while the catch-all verdict still points at it → **use-after-free**.
Chaining a kernel-address leak, arbitrary R/W, and a ROP over
`modprobe_path` / `selinux_state` turns the UAF into root — all reachable
by an unprivileged user who has `CONFIG_USER_NS` to gain `CAP_NET_ADMIN`
over a private network namespace.
## Affected range
| | |
|---|---|
| Vulnerable path introduced | ~5.13 (catch-all set elements) |
| Fixed upstream | commit `f41c5d1…` (remove the inverted `!`) |
| Debian backports | 6.1.164 (bookworm) · 6.12.73 (trixie) · 6.18.10 (forky·sid) |
| Table thresholds | 6.1.164 · 6.12.73 · 6.18.10 (≤ Debian → drift-clean) |
| NVD class | CWE-416 (Use After Free), CVSS 7.8 |
| CISA KEV | no |
The 5.10 (bullseye) branch is still unfixed at time of writing →
version-only VULNERABLE there.
## Trigger / detection
`detect()` returns `OK` below ~5.13 or for patched kernels, `PRECOND_FAIL`
when the kernel is vulnerable but unprivileged user-namespace clone is
denied (exploit unreachable), and `VULNERABLE` when the version is in
range and userns is allowed.
`exploit()` forks an isolated child that enters `unshare(USER|NET)`, opens
`NETLINK_NETFILTER`, builds a verdict map with a catch-all GOTO element,
and sends an aborting batch to drive the abort-path UAF; it observes
`nft_chain` / `kmalloc-cg-256` slabinfo and returns `EXPLOIT_FAIL`
(primitive-only). The full leak + R/W + ROP root-pop is **not** bundled,
and the trigger is reconstructed from public analysis, not VM-verified.
## Fix / mitigation
Upgrade the kernel. As a host hardening stopgap, deny unprivileged
user-namespace clone (`sysctl kernel.unprivileged_userns_clone=0`, or the
AppArmor `apparmor_restrict_unprivileged_userns` toggle) — that closes the
unprivileged path even on a kernel-vulnerable host.
## Credit
Upstream fix `f41c5d1…`; public reproduction by FuzzingLabs. See
`NOTICE.md`.