CVSS 9.8 (kernel CNA) · remote · use-after-free · not on CISA KEV

Linux TCP Use-After-Free (CVE-2026-98432): Ubuntu Still Unpatched, How to Check and Mitigate

5 November 2026 · 11 min read

A race in how the Linux kernel builds the reply to a TCP connection request can free memory that is still being read. It has been there since Linux 4.4 in 2016, the kernel's own CVE team scores it 9.8, and Anthropic has shown it turned into remote root on a server with a multipath TCP listener. Upstream fixed it on October 1, and Debian, Red Hat, SUSE and Amazon have all shipped kernels. Ubuntu has not. Three weeks after the CVE, the fixed Ubuntu LTS kernels are still in the -proposed pocket, so a fully updated Ubuntu 24.04 server runs a vulnerable kernel today. We stood one up and checked. This post covers who is exposed, how to tell, and a one-rule mitigation that works while you wait.

The Vulnerability

CVE-2026-98432 is a use-after-free in the IPv4 TCP stack (the CVE record carries no CWE; Red Hat classifies it CWE-825, Expired Pointer Dereference). It needs two things to line up. The first is a connection request (a SYN) that carries IPv4 header options, such as Record Route or Timestamp. When Linux answers such a SYN, it keeps a copy of those options with the half-open connection and echoes them back in its SYN-ACK. The second is timing. The fix commit, by Eric Dumazet, explains the race:

"tcp_v4_syn_recv_sock() transfers ownership of ireq->ireq_opt to the child socket (newinet->inet_opt) without copying it. Another cpu can concurrently process a retransmitted SYN for the same request socket, and send a SYNACK"

So one CPU completes the handshake and hands the options to the new connection, while another CPU, answering a retransmitted SYN, is still building a SYN-ACK from the same options. If the new connection closes in that window, the kernel frees the options with a plain kfree() and the SYN-ACK is built from freed memory in ip_options_build(). The fix is two lines: free with kfree_rcu() instead, so the memory outlives every reader. The commit traces the bug to 079096f103fa, which landed in Linux 4.4, and fixes a second variant in request-socket migration (net.ipv4.tcp_migrate_req=1, off by default). IPv6 is not affected by the main bug, because IPv6 copies the options instead of handing them over. That protects only connections that actually arrive over IPv6: a dual-stack socket bound to [::] still handles IPv4 connections through the IPv4 path.

  • CVSS: 9.8 Critical (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), scored by the Linux kernel CNA; NVD has not published its own. Red Hat and Ubuntu rate it Critical; SUSE and Amazon rate it Important (8.1, scoring the race as AC:H).
  • CWE: none in the CVE record; Red Hat classifies it CWE-825 (Expired Pointer Dereference). A use-after-free in the ordinary sense.
  • NEEDS: an IPv4 TCP listener, and a SYN carrying IPv4 header options. No credentials, no special sysctl, no MPTCP for the bug itself.
  • AFFECTED: Linux 4.4 and later, up to the fixed stable releases
  • FIXED UPSTREAM: 7.3-rc7 · 7.2.10 · 6.18.56 · 6.12.113 · 6.6.159 · 6.1.190 · 5.15.223 · 5.10.272
  • UBUNTU: fixed kernels in -proposed only (24.04 6.8.0-149.149, 22.04 5.15.0-203.213, 26.04 7.0.0-43.43); no USN
  • EXPLOITED: no confirmed exploitation in the wild; not on CISA's KEV list. Anthropic demonstrated remote root against an MPTCP listener; we found no public exploit code.
  • RECORD: kernel CNA, published October 15, 2026; fix commit e3f33b0a1d89, October 1

Am I exposed?

Affected
Any Linux server listening on IPv4 TCP, running a kernel without the fix. On Ubuntu that is every LTS release on its normal update channels today: 24.04 up to 6.8.0-147, 22.04 up to 5.15.0-202. Servers with an MPTCP listener are the ones with a demonstrated route to root, and Go 1.24+ programs open one by default.
Not you
Kernels carrying your vendor's fix: Debian 13 6.12.113-1+, Debian 12 6.1.190-1+, RHEL 9 5.14.0-687.64.1+, RHEL 8 4.18.0-553.182.1+, RHEL 10 6.12.0-211.72.1+, current Amazon Linux (RHEL figures are the main streams; EUS, AUS and TUS streams have their own builds, shipped November 2 to 4, so check the RHSA for your stream). Only listeners that cannot be reached over IPv4 at all sit outside the main bug: a socket bound to [::] still accepts IPv4 connections and is in scope, as *:8080 in our transcript is.
Check
On the server: uname -r, then compare against your vendor's fixed version (not the upstream number; distros backport). Installing a fixed kernel does nothing until you reboot into it.
Fix
Install your vendor's fixed kernel and reboot. On Ubuntu, until the fix leaves -proposed, drop TCP packets carrying IPv4 options with the one-rule nftables table below. In our test it blocked the probe and left ordinary traffic working.

Who Has Patched, and Who Has Not

The fix was public upstream on October 1 and in every maintained stable branch by October 15, the day the CVE was published. What a server actually runs depends on its distribution, and here the picture splits:

Every other major distro shipped the fix within 17 days. Ubuntu LTS has not.

Upstream fix (reference) · public Oct 1 · stable kernels Oct 11–15
Debian 13 and 12 · shipped Oct 16
Amazon Linux · shipped Oct 24–30
SUSE (SLES 15 LTSS) · shipped Oct 29
Red Hat Enterprise Linux · shipped Nov 1
Ubuntu 22.04 / 24.04 / 26.04 · fix built, held in testing (-proposed) since Oct 26; not on normal updates as of Nov 5
Dates are when each vendor's fixed kernel reached its normal update channel. Sources: the fix commit, vendor advisories and trackers.
shipped (first fixed kernel)
Debian 13    6.12.113-1
Debian 12    6.1.190-1
Amazon Linux AL2, AL2023
SLES 15 SP6  6.4.0-150600.23.143.1
  LTSS
RHEL 8.10    4.18.0-553.182.1.el8_10
RHEL 9.8     5.14.0-687.64.1.el9_8
RHEL 10.2    6.12.0-211.72.1.el10_2

in -proposed only (no USN)
Ubuntu 24.04 6.8.0-149.149
Ubuntu 22.04 5.15.0-203.213
Ubuntu 26.04 7.0.0-43.43

Ubuntu 20.04  HWE kernel (Pro)
  5.15.0-203.213~20.04.1

not fixed, no build
Ubuntu 20.04  GA 5.4 kernel (Pro)

Ubuntu's tracker rates the CVE critical and lists the main LTS kernels as pending, with fixed builds in -proposed since October 26 for 22.04 and 24.04. In a November 2 post Canonical says the delay is testing: the kernels are going through validation on more than 2,000 certified servers and devices under what it calls its "new SRU strategy for accelerating CVE fixes", and it gives no release date. The same post lists the interim 26.10 release as already fixed, though Ubuntu's CVE tracker still shows that build as pending. Red Hat's fixed RHEL kernels shipped on November 1, with separate builds for its extended-support streams; Debian has since shipped newer kernels on top of the first fixed ones above. Red Hat OpenShift's CoreOS nodes were still listed as affected when we checked.

The -proposed pocket is Ubuntu's staging area for updates that have not finished testing, and it is off by default. A server that runs apt upgrade every night, exactly as it should, does not get this fix. That is the gap this post is about.

What We Reproduced on a Real Ubuntu Server

We stood up a throwaway Ubuntu 24.04 virtual machine, applied every update from the normal channels, rebooted, and checked each claim above. We did not trigger the race itself. On a stock kernel a lost race leaves no trace, and we found no public trigger that works reliably on a stock kernel. Building a remote trigger for a bug that is still unpatched on Ubuntu is not something we will do for a blog capture. What we can show is everything around the race: that the kernel is vulnerable, that the code path is reachable by a single unauthenticated packet, which listeners have the demonstrated route to root, and that the mitigation removes it.

# after apt full-upgrade and a reboot
$ uname -r
6.8.0-147-generic
$ apt-cache policy linux-image-6.8.0-149-generic
  Candidate: 6.8.0-149.149
     6.8.0-149.149 400
        400 http://ports.ubuntu.com/ubuntu-ports
            noble-proposed/main arm64 Packages

$ sysctl net.mptcp.enabled
net.mptcp.enabled = 1

# :8081 is python3 -m http.server, :8080 plain Go
$ ss -ltni '( sport = :8080 or sport = :8081 )' \
    | grep -E 'LISTEN|ulp'
LISTEN 0  5     0.0.0.0:8081  0.0.0.0:*
LISTEN 0  4096        *:8080        *:*
	 cubic cwnd:10 tcp-ulp-mptcp

# one SYN with an IPv4 Record Route option
$ python3 syn_rr.py
reply flags=SA ip_options=['IPOption_RR', 'IPOption_EOL']
# tcpdump -v of the reply, abridged
10.200.0.1.8080 > 10.200.0.2.40001: Flags [S.]
  options (RR 10.200.0.1, 10.200.0.1, 0.0.0.0,EOL)

Four things in that transcript matter. The newest kernel the normal channels offer is 6.8.0-147, and the fixed 6.8.0-149 is a candidate only from noble-proposed. MPTCP is enabled by default. An ordinary Go web server, which never asks for MPTCP, carries tcp-ulp-mptcp on its listening socket, while a Python server next to it does not. And a single SYN with a Record Route option came back as a SYN-ACK with the option rebuilt and filled in by the kernel: the reply was assembled through tcp_v4_send_synack() and ip_options_build(), the path where the freed memory is read. Any unauthenticated client can put the kernel on that path. Reaching it is not the same as winning the race, which needs a retransmitted SYN and the new connection closing at the wrong moment.

We then installed 6.8.0-149 from noble-proposed. Its changelog carries the upstream commit, as part of Ubuntu's "upstream stable patchset 2026-10-21", and does not name the CVE. If you search kernel changelogs for CVE-2026-98432, you will not find it; search for "free inet_opt and ireq_opt". After rebooting into 6.8.0-149 we sent the same Record Route SYN, and the kernel still answered with a SYN-ACK echoing the option. That is correct: the fix changes when the memory is freed, not what goes on the wire. It also means no packet you send can tell a patched kernel from an unpatched one.

MPTCP Is the Exploit's Door, Not the Bug's

Two claims are circulating that are both partly right. Anthropic's write-up, from the team whose researcher reported the bug, describes an exploit that lets "a remote attacker to send packets to Linux servers with a multipath TCP listener to run arbitrary commands as root, without any credentials or user interaction." In their proof of concept it took 48 minutes to leak a kernel pointer and five more to run commands, and a lost race "only fails the handshake," so an attacker can keep trying. That is the strongest public evidence of what the bug can do, and it is specific to MPTCP listeners.

The bug itself is not. Red Hat's bulletin is direct: "Having MPTCP enabled is not a precondition of this vulnerability," and disabling it with sysctl -w net.mptcp.enabled=0 "only blocks one method of exploitation." The race is in ordinary IPv4 TCP. So the honest reading is: every IPv4 TCP listener is affected; MPTCP listeners are where a working root exploit has been shown.

That makes the Go detail more than a footnote. The Go 1.24 release notes say ListenConfig "now uses MPTCP by default on systems where it is supported (currently on Linux only)." Every service built with Go 1.24 or later and deployed on Ubuntu, where MPTCP is on, is listening as MPTCP unless someone turned it off. That covers a large share of modern infrastructure tooling. Go lets you turn it off per program without a rebuild: GODEBUG=multipathtcp=0 restores the pre-1.24 behaviour. To list them on a host, look for tcp-ulp-mptcp in ss -ltni. An empty result lowers the priority; it does not remove the bug.

The Mitigation That Works While You Wait

Legitimate TCP traffic almost never carries IPv4 header options, and the bug needs them. Red Hat's bulletin publishes an nftables table that drops TCP packets whose IPv4 header is longer than the 20-byte minimum, which is what options make it:

table inet cve_2026_98432 {
  chain input {
    type filter hook input priority -310; policy accept;
    ip protocol tcp ip hdrlength > 5 counter drop
  }
}

Save it as cve-2026-98432.nft, check it with sudo nft --check --file cve-2026-98432.nft, and load it with sudo nft --file cve-2026-98432.nft. In our test the same Record Route SYN that had drawn a SYN-ACK then got no reply, the rule's counter recorded the drop, and an ordinary HTTP request to the same Go server still returned ok.

Canonical does not recommend this. Its post says "security updates are the only appropriate mitigation" and points Ubuntu users at the kernels in -proposed. Red Hat publishes the rule, and we tested it. The two are not exclusive: the filter covers the window until a host has rebooted into a fixed kernel, from -proposed or later from the normal channels. Decide which advice fits your estate.

Know its limits before relying on it; Red Hat itself presents it as reducing exposure, not as a replacement for the fixed kernel. It is not persistent: it disappears on reboot unless you add it to your nftables configuration. It filters traffic entering the host's network namespace, so Red Hat notes that pod-to-pod traffic inside an OpenShift cluster is not covered, and the same caution applies to container-to-container traffic on any host. By construction it also drops CIPSO-labelled traffic, which carries an IP option; almost nobody runs it. Turning on init_on_free=1 does not help; that was rebutted on oss-security. And Ubuntu users can install the fixed kernel from -proposed today, which is the same build Canonical is testing, at the cost of running a kernel that has not finished that testing.

What the Headlines Got Wrong

A November 2 Hacker News thread called it a "Linux 0day." By the time that headline ran it was not one: the patch was posted publicly on October 1 and the CVE published on October 15. The story is closer to the opposite of a zero-day. Everyone has known about this bug and its fix for weeks, and one major distribution has not shipped the fix to its default update channels. A zero-day is a vendor problem; this is a patch-pipeline problem, and it is the one most Ubuntu administrators can act on today.

We also have not seen evidence of exploitation in the wild. One write-up cites a researcher reporting MPTCP scanning of port 443 on November 3; we could not find the primary source, so we are not repeating it as fact. The bug is not on CISA's KEV list. Whether a SYN carrying IP options survives routing across the public internet to your server is also not established by any source we found, and we are not going to guess. The safe assumption is that it can.

Investigation Workflow

The kernel version is not visible from the network. No banner, certificate or TCP fingerprint tells you whether a server carries the fix, so this is an inventory exercise first and a network one second.

1. Inventory Kernels, Not Packages

uname -r on each host is the only answer that counts, because an installed-but-not-booted kernel protects nothing. Compare against your distribution's fixed build. On Ubuntu, everything on the normal channels is affected right now.

2. Port Scan: Rank by What Listens

Every IPv4 TCP listener reachable from an untrusted network is in scope. Run RECON's port scan against each server from outside the network to see which listeners are actually reachable, then rank internet-facing hosts first. On each host, ss -ltni with tcp-ulp-mptcp marks the services with the demonstrated route to root; Go services built with 1.24 or later are the usual suspects.

3. Confirm the Mitigation From Outside

A filter that only exists on paper is not a mitigation. Send a SYN with an IPv4 option to a port you own and confirm no SYN-ACK comes back; the scapy line in our test was IP(dst=host, options=[IPOption_RR()])/TCP(dport=port, flags="S"). A SYN-ACK means the rule is not in the path, whether because it was lost on reboot or because the traffic arrives through a namespace the rule does not cover. Do this only against systems you are responsible for.

Remediation

  1. Patch and reboot where a fix has shipped. Debian, RHEL and its rebuilds, SUSE and Amazon Linux all have fixed kernels. Confirm with uname -r after the reboot.
  2. On Ubuntu, close the window until you can reboot into a fixed kernel. Canonical's answer is the -proposed kernel; for internet-facing hosts with MPTCP listeners it may well be the better trade than waiting, but it is a risk decision, not a default. The other option is the nftables rule: if you use it, make it persistent through reboots until a fixed kernel ships to the normal channels, then remove it with sudo nft delete table inet cve_2026_98432.
  3. Find MPTCP listeners. ss -ltni and look for tcp-ulp-mptcp. Where MPTCP is not needed, setting net.mptcp.enabled=0 removes the demonstrated exploit path for new sockets, but it is not a fix for the bug. For a single Go service, GODEBUG=multipathtcp=0 does the same.
  4. Do not forget containers and clusters. Container hosts and Kubernetes nodes run the host kernel; the nftables rule does not cover traffic between pods or containers on the same host.

Triage Notes

  • Remote recon proves: which IPv4 TCP listeners are reachable, and whether a SYN carrying IP options draws a SYN-ACK. A SYN-ACK means the mitigation is not in front of that listener; it says nothing about the kernel, since patched kernels answer the same way.
  • It cannot prove: the kernel version, whether a fixed kernel is installed and booted, or whether a service is an MPTCP listener.
  • Evidence to request: uname -r per host; the distribution and its fixed-version reference; ss -ltni output; sysctl net.mptcp.enabled; the nftables ruleset and how it is persisted.
  • Escalation threshold: an internet-reachable IPv4 TCP listener on an unfixed kernel without the options filter. An MPTCP listener on top of that is the configuration with a demonstrated remote root.
  • Finding statement: "The host runs a Linux kernel without the fix for CVE-2026-98432, a use-after-free in IPv4 TCP connection setup that the kernel CNA scores 9.8, and accepts IPv4 TCP connections from untrusted networks. Install the distribution's fixed kernel and reboot; where none has shipped (Ubuntu LTS as of November 5, 2026), drop TCP packets carrying IPv4 options with the nftables rule published in Red Hat RHSB-2026-016 and make it persistent."

Follow @hellorecon for new CVE investigations.

Sources

By Vladimir Slavin · Founder, RECON · [email protected]

Related guides and tools