
Interim nftables mitigation for Linux kernel CVE-2026-98432 (IPv4-options UAF in tcp_v4_syn_recv_sock) as a reproducible agent skill
Agent skill that applies, verifies, persists, and later removes the interim host mitigation
for Linux kernel CVE-2026-98432 (CVSS 9.8): a single nftables rule dropping TCP packets
carrying IPv4 options — the only input that reaches the inet_opt/ireq_opt
use-after-free in tcp_v4_syn_recv_sock().
Context worth knowing: Anthropic's Frontier Red Team publicly described a working
unauthenticated-to-root PoC against servers exposing an MPTCP listener (Oct 2026); no
in-the-wild exploitation reported. The kernel CNA scores 9.8; upstream and Red Hat score 8.1
(attack complexity high). The vendor fix is Ubuntu kernel 6.8.0-149-class backports —
until the running kernel carries the fix, this rule is the stopgap. It is scoped to one
field of one header of one protocol, so normal traffic is untouched.
This rule is not novel — treat it as packaged prior art. See "Prior art" below; this repo's contribution is the reproducible agent-skill form plus the production pitfalls.
| Path | What |
|---|---|
SKILL.md | The skill — frontmatter triggers, ruleset, checks, persistence, pitfalls, removal |
files/cve98432.nft | Copy-ready nftables ruleset (prerouting, priority −300) |
files/nft-cve98432.service | Copy-ready systemd unit (persistence) |
cp -R cve-2026-98432-nft-mitigation ~/.claude/skills/ # Claude Code
cp -R cve-2026-98432-nft-mitigation ~/.agents/skills/ # agents-style runtimes
The description frontmatter is the discovery surface: agents select this skill when asked
to mitigate CVE-2026-98432, the "TCP/IP fix", the kernel IPv4-options UAF, or to remove the
mitigation after the vendor kernel ships.
zgrep -c CVE-2026-98432 /usr/share/doc/linux-modules-$(uname -r)/changelog.Debian.gz # Debian/Ubuntu
rpm -q --changelog kernel-core-$(uname -r) | grep -c CVE-2026-98432 # RHEL/Rocky/Alma
> 0 = fixed, skip the mitigation. (Also check your vendor tracker; and
apt-cache policy linux-image-generic for an installed-but-not-booted newer kernel.)ss -lnMp): the published PoC targets them, and Go ≥ 1.24 listens
with MPTCP by default on kernels that allow it. sysctl net.mptcp.enabled=0 only affects
new sockets — already-open listeners keep MPTCP until their service restarts
(GODEBUG=multipathtcp=0 per the Go team).cat /sys/kernel/security/lsm lists SELinux or Smack and
you use NetLabel CIPSOV4 mapping, IPv4 options are legitimate there — this rule would break
that path. Do not apply on CIPSO-labeled networks (RFC 7126 discusses the trade-off).files/cve98432.nft and files/nft-cve98432.service.nft -c -f /usr/local/sbin/cve98432.nft — parse check; requires root (non-root fails
at netlink: cache initialization failed).systemctl daemon-reload && systemctl enable --now nft-cve98432.servicedestroy table … line must be the
first line of the file: appended at the end it creates the table and then destroys it,
leaving an empty ruleset while the service still exits 0.nft list table inet cve98432 must show
meta l4proto tcp ip hdrlength > 5 counter drop on a chain with priority -300
(before conntrack, matching the published rule).table inet … is not owned by iptables, so iptables-restore /
ufw / firewalld tooling leaves it intact (run your restore once and re-list to confirm).Removal after the fixed kernel is installed and rebooted: section 7 of SKILL.md.
nftables state is per network namespace. Host rules see a container's traffic only where
it crosses the host (e.g. container → outside); traffic that stays inside the container's
own namespace (container → its own loopback/IP) never traverses the host's prerouting —
yet the kernel is shared, so code inside a container can still reach the UAF locally. Inject
the same ruleset into every running container:
# Docker (host-side nsenter keeps the needed capabilities)
for c in $(docker ps -q); do
pid=$(docker inspect -f '{{.State.Pid}}' "$c")
nsenter -t "$pid" -n /usr/sbin/nft -f /usr/local/sbin/cve98432.nft
done
# LXC (privileged CLI as root)
for c in $(lxc-ls -1 2>/dev/null); do lxc-attach -n "$c" -- nft -f /usr/local/sbin/cve98432.nft; done
Container namespaces do not persist nft rules — re-run on every container restart (hook
it into your orchestrator's post-start step). Prior art for this exact gap, with scripts:
typhoon1217/cve-2026-98432-mitigation (inject-netns.sh).
Standard sockets suffice — no packet crafting on the server: a client sets an IPv4 Record
Route option via setsockopt(IP_OPTIONS) and connects; a plain connect succeeds, the
options connect must time out while the rule's counter increments. Ready-made clients:
check-blocked.py / verify-synack.sh in typhoon1217/cve-2026-98432-mitigation.
Caveat from that project: a client that itself runs the mitigation will also drop the
target's SYN-ACK echoes — test from an unmitigated client for a clean verdict.
prerouting at priority -300: before conntrack, so dropped packets never create
conntrack state — and, unlike input, it also covers traffic forwarded to containers/VMs
that crosses the host.@nh,4,4 > 5 to
ip hdrlength > 5 — the IHL field, confirmed by the tool itself.typhoon1217/cve-2026-98432-mitigation — same drop rule + container-netns injection,
CIPSO caveat, verification clients (EN/KO)Distilled from a production deployment, including failures hit there: non-root nft -c, the
destroy-at-end ordering bug, an unquoted heredoc silently eating \$, and double-apply
semantics. Docs-only repository — runtime dependencies: nft ≥ 1.0 + systemd; no test suite
(n/a).