On 2026-10-29 The Hacker News reported CVE-2026-98432: a CVSS 9.8 use-after-free in the Linux kernel’s IPv4 TCP handshake, reachable by an unauthenticated remote attacker. Every one of our servers runs Ubuntu, and Ubuntu had no fixed kernel yet.
This post covers what the bug is, the firewall-level mitigation we rolled out to every platform server in about 25 minutes with zero downtime, and what we are changing so we hear about the next one sooner.
The bug in one paragraph
When a TCP connection completes, the kernel hands the IP options stored on the pending request to the new socket without copying them. If that socket is destroyed while another CPU is still using those options to answer a retransmitted SYN, the kernel reads freed memory. An attacker triggers it by sending SYNs that carry the IPv4 Record Route or Timestamp option, and resending them until the race is won.
A few things make it nasty:
- It fires before any application sees the connection. Any listening TCP port is exposed, SSH included. No application code can defend against it.
- Losing the race is free. A failed attempt just drops that one connection, so an attacker can retry forever.
- It’s old. Present since Linux 4.4. IPv6 is not affected.
- It’s been exploited in a lab. Anthropic published a root exploit (found and written by Claude) against servers with a multipath TCP (MPTCP) listener: about 48 minutes to leak a kernel address and defeat KASLR, then 5 more minutes to run commands as root. The Go team notes the underlying bug is not MPTCP-specific.
No exploitation in the wild had been reported at the time of writing.
Vendor scores disagree, mostly on attack complexity: the kernel CVE team, NVD and Ubuntu say 9.8; Oracle and SUSE say 8.1; Amazon says 7.0; Red Hat says 5.9 on its CVE page and 8.1 in its advisory. We treated it as critical.
Timeline
| Date | Event |
|---|---|
| 2026-10-05 | Upstream kernel fix committed |
| 2026-10-15 | CVE published. Ubuntu priority: High |
| 2026-10-24 | Kernel CVE team adds the 9.8 score |
| 2026-10-27 | Anthropic publishes the MPTCP root exploit write-up |
| 2026-10-28 | The Hacker News covers it |
| 2026-10-29 | Ubuntu and RHEL still have no fixed kernel |
| 2026-10-30 | We see the article, audit, and mitigate every platform server in ~25 min |
Note the gap: we learned about it 15 days after the CVE was published, from the news. More on that below.
The audit
A read-only pass over every server answered four questions:
- Which kernel? All Ubuntu (24.04 and 26.04), all unpatched. Ubuntu’s tracker listed the fix as in progress for
6.8.0-149(24.04) and7.0.0-43(26.04). - Is MPTCP on? Yes,
net.mptcp.enabled = 1everywhere. That’s the Ubuntu default, and we don’t use MPTCP. Is anything actually listening with MPTCP? No, on every host and in every container:
ss -HlnM # on the host for c in $(docker ps -q); do # inside each container's network namespace pid=$(docker inspect -f '' "$c") nsenter -t "$pid" -n ss -HlnM doneSo the specific exploit path Anthropic demonstrated was likely closed. The general race was still open.
- Does any firewall filter IPv4 options already? No.
The mitigation
Two parts, applied on every server.
1. Drop IPv4 TCP packets that carry IP options. Legitimate internet traffic essentially never sends them, and they are the documented trigger. An IPv4 header without options is exactly 5 words long, so anything longer has options:
# /etc/nftables.d/cve-2026-98432.nft
table ip cve_2026_98432
delete table ip cve_2026_98432
table ip cve_2026_98432 {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
ip protocol tcp ip hdrlength != 5 counter drop
}
}
It lives in its own table so it never touches Docker’s rules, runs at raw priority (-300) so it sees packets before conntrack and Docker’s NAT, and the first two lines make the file safe to load repeatedly.
A small oneshot unit loads it on boot, before Docker:
# /etc/systemd/system/cve-2026-98432.service
[Unit]
Description=Mitigation for CVE-2026-98432 (drop IPv4 TCP with IP options)
DefaultDependencies=no
Before=network-pre.target docker.service
Wants=network-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/nft -f /etc/nftables.d/cve-2026-98432.nft
ExecStop=/usr/sbin/nft delete table ip cve_2026_98432
[Install]
WantedBy=multi-user.target
2. Turn off what we don’t use.
# /etc/sysctl.d/99-cve-2026-98432.conf
net.mptcp.enabled = 0
net.ipv4.tcp_migrate_req = 0
Load only this file:
sysctl -p /etc/sysctl.d/99-cve-2026-98432.conf
The generic advice is sysctl --system, but that re-applies every sysctl file on the box. On a Docker host that can reset settings Docker changed at runtime (for example net.ipv4.ip_forward) and quietly break container networking. We caught this before running it.
One more gotcha: host sysctls don’t reach container network namespaces. If you need MPTCP off inside containers too, set it per container (sysctls: { net.mptcp.enabled: 0 } in Compose).
Rolling it out without locking ourselves out
Firewall changes on remote servers are how you lose SSH. Each server got the same routine, lowest risk first (CI runner → staging DB → app standby → app primary → production DB):
- Pre-check the current state.
Arm a 10-minute auto-undo before changing anything:
systemd-run --unit=cve-undo --on-active=10m /bin/sh -c \ 'nft delete table ip cve_2026_98432; sysctl -w net.mptcp.enabled=1 net.ipv4.tcp_migrate_req=1'- Apply the rule and sysctls, enable the unit.
- Verify from a fresh SSH session, plus the service itself: CI runners active, databases accepting new connections, containers healthy, the production site loading normally from outside,
ip_forwardunchanged. - Only then cancel the undo:
systemctl stop cve-undo.timer.
If any step had cut us off, the server would have reverted itself within 10 minutes. It never came to that: no restarts, no errors, no customer impact.
The counter on the rule tells you whether anyone has tried:
nft list table ip cve_2026_98432 # non-zero packets = attempts were blocked
Do the apps need changes? No.
The vulnerable code runs before any application touches the connection. The only app-level guidance in the coverage is for Go 1.24+, which listens with MPTCP by default: set GODEBUG=multipathtcp=0. Our services are Node.js and Python, with no Go code, and the listener check above confirmed nothing uses MPTCP. The Go team itself calls per-app MPTCP disabling “fragile”, which is another reason to fix it at the host.
This is a stop-gap
No vendor endorses a mitigation for this CVE; Red Hat says none meets its standards. Our rule removes the documented trigger, but the real fix is the patched kernel and a reboot. We’re tracking Ubuntu’s CVE page until it ships.
What we’re changing
The 25-minute rollout went well. The 15 days before it didn’t.
- Hear about it from the vendor, not the news. Subscribe to Ubuntu Security Notices (USN). Security newsletters help too, but they’re a backup, not the feed.
- Actually run the kernels we install. Every server had a reboot pending for kernel updates that were already installed. Installed is not the same as patched. We’re enabling
unattended-upgradeswith a scheduled reboot window and evaluating Canonical Livepatch. - Disable unused kernel features by default. MPTCP was on because it’s the default, not because we needed it. Nothing used it this time, but a future Go service could have opened an MPTCP listener without anyone noticing.
- Keep a server inventory. “Fix all Linux servers today” is only actionable if you know what all your Linux servers are, who owns each, and how to reach it.
References
- The Hacker News: Linux Kernel TCP Flaw With 9.8 CVSS Score Affects Ubuntu, Red Hat and Other Distributions
- Ubuntu CVE tracker: CVE-2026-98432