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
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.
63 lines
2.6 KiB
Markdown
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`.
|