← All writing

LINUX KERNEL

CVE-2026-98432: a quiet Linux TCP race that an AI turned into remote root

Read on Medium

Written 29 October 2026. Distro patch status is still moving, so check your vendor's tracker before acting on the table below.

On 15 October a tiny fix landed in the Linux kernel: free two IPv4 option structures after an RCU grace period instead of immediately. Twelve days later, Anthropic's Frontier Red Team said an unreleased Claude model had turned the bug behind it into a zero-click remote root exploit.

That gap, between how boring the patch looks and what the exploit does, is the interesting part of this story.

TL;DR

  • What: a race-condition use-after-free in the Linux kernel's IPv4 TCP handshake code.
  • Where: ip_options_build(), reading IP options that another CPU has already freed.
  • How old: introduced by commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table"). Affected ranges in public databases start at kernel 4.4.
  • Severity: depends on who's scoring. Kernel CNA: 9.8. Ubuntu (CVSS v4): 8.3. Red Hat: Moderate.
  • Exploited? No known in-the-wild exploitation and not in CISA KEV at the time of writing. Anthropic reports a working lab exploit against servers with a multipath TCP listener.
  • Fix: upstream commit e3f33b0a1d89 (merged for 7.3-rc7), backported to stable trees. Update your kernel.

The bug, in plain terms

When a TCP client connects, the server keeps a small "request socket" for the half-open connection. If the client's SYN carried IPv4 options, those options are stored on that request socket (ireq->ireq_opt).

When the handshake completes, tcp_v4_syn_recv_sock() creates the real child socket and hands the options over to it. It doesn't copy them, it just transfers the pointer.

Here's the race:

  1. CPU A completes the handshake. The child socket now owns the options.
  2. CPU B is processing a retransmitted SYN for the same request socket and starts building a SYNACK. It reads those same options under rcu_read_lock(), and ip_options_build() reads opt->optlen twice.
  3. The child socket gets destroyed. inet_sock_destruct() frees the options with a plain kfree().
  4. CPU B is still reading them. That's the use-after-free.

Why did nothing stop this? Since 079096f103fa, request sockets are processed without holding the listener lock. That was a deliberate scalability change. Readers were already using RCU, but the destructor wasn't waiting for an RCU grace period, so the RCU protection on the read side wasn't actually protecting anything.

There's a second path to the same bug: request socket migration (net.ipv4.tcp_migrate_req=1, or a BPF_SK_REUSEPORT_SELECT_OR_MIGRATE program). Migration clones the request socket so the old one and its clone share the same options pointer. If the clone is freed while another CPU is still using the old socket, same result.

The fix is small: free both structures with kfree_rcu() instead of kfree(), matching what other code paths that replace inet_opt already did.

IPv6 isn't affected by the first issue, because tcp_v6_syn_recv_sock() duplicates the options instead of transferring them. The IPv6 migration path has a similar issue, which upstream says will get its own patch.

Why the severity scores disagree so much

Source Score Notes
Kernel CNA 9.8 (CVSS 3.x) Network, no auth, no user interaction, full impact
Ubuntu 8.3 (CVSS 4.0) Marks "attack requirements: present", low confidentiality/integrity, high availability
Red Hat Moderate Describes it as a race needing specially timed requests

Same bug, three very different numbers. The disagreement is about how hard the race is to win and whether it realistically goes beyond a crash.

Before 27 October, the reasonable reading was "probably a hard-to-hit DoS." Then Anthropic published a chain that turns it into root.

This is the same lesson as last week's GitLab and Plex bugs: a CVSS number is a judgment made at one moment with the information available. It doesn't update itself when someone proves the bug is worse than it looked.

What Anthropic reported

On 27 October, Anthropic's Frontier Red Team published a post describing five exploit chains built by an unreleased internal Claude research model. The Linux one, as reported:

  • Target: servers running a multipath TCP (MPTCP) listener.
  • Zero-click: no login, no user interaction.
  • Step 1: use the race to leak a kernel pointer and defeat KASLR. About 48 minutes in their test.
  • Step 2: reuse the same race to overwrite a callback pointer and run a command as root. About five more minutes.
  • Why it's retryable: losing the race just fails that handshake. It doesn't crash the machine, so an attacker can keep trying.

One caveat worth being precise about: the public CVE record describes an IPv4 TCP fix and never mentions MPTCP. The link between this CVE and the MPTCP chain comes from Anthropic's post. Their full exploit details aren't public, so I'm not going to guess at how the MPTCP path reaches the vulnerable code.

What's not in doubt: a bug that several vendors scored as a moderate race condition was turned into reliable remote root, with very little human involvement according to Anthropic.

Am I affected?

Very likely, if you run Linux and haven't updated since mid-October. Affected ranges in public databases cover kernels from 4.4 onward until each stable branch's fixed release.

Distro status as of late October:

  • Upstream / stable: fixed. Check that your kernel includes e3f33b0a1d89 or your branch's backport.
  • Ubuntu: fixes marked "work in progress" for most supported releases and cloud kernels when I checked. 14.04 Trusty is listed as not affected.
  • RHEL 8, 9, 10 (including Real Time), RHEL CoreOS / OpenShift 4: affected, rated Moderate by Red Hat.
  • Azure Linux / CBL-Mariner: fixed in 6.6.159.1-1.
  • Debian 13: TuxCare lists a KernelCare live patch as released.

What to do

  1. Patch. Install your distro's fixed kernel as soon as it ships, and reboot (or use a live-patching service if you have one).
  2. Know if you run MPTCP listeners. The published chain needs one. ss -lM lists listening MPTCP sockets on recent iproute2. If you don't use MPTCP, sysctl net.mptcp.enabled=0 prevents new MPTCP sockets from being created. That narrows exposure to the published chain. It does not fix the underlying bug.
  3. Leave net.ipv4.tcp_migrate_req at its default of 0 unless you know you need it. That removes the second path.
  4. Don't wait on the score. Your scanner may show this as Moderate. Given a public report of a working root chain, treat it as high priority for internet-facing hosts.

The bigger picture

This is the part I keep thinking about.

The patch is two kfree calls becoming kfree_rcu. The bug was a subtle race in code that's been around for roughly a decade. Several vendors looked at it and reasonably said "moderate." And an AI model went from that to a reliable remote root chain.

Two takeaways:

  1. Severity scores lag reality. They're best guesses about exploitability, and the cost of building exploits just dropped. "Hard to exploit" is a weaker reason to delay a patch than it used to be.
  2. Patch speed is now the bottleneck. Finding bugs is getting cheap. Shipping and deploying fixes isn't. For most teams, the most useful security improvement this year is shrinking the time between "upstream fixed it" and "it's running on my servers."

Sources: Ubuntu CVE tracker · Upstream fix e3f33b0a1d89 · Red Hat · The Hacker News on Anthropic's exploit chains · Cyber Security News · TuxCare · pwn.watch

More writing Read on Medium