Why Go 1.24's Default MPTCP Exposed Linux Hosts to CVE-2026-98432
When Anthropic demonstrated Claude synthesizing a remote root exploit over raw TCP, most systems engineers did not panic.
The vulnerability, tracked as CVE-2026-98432 with a Common Vulnerability Scoring System (CVSS) score of 9.8, targeted Multipath TCP (MPTCP) listeners. Standard advice sounded simple. If your fleet runs plain TCP web servers, you do not touch MPTCP subflows. You can safely wait for distribution kernel patches.
Then the Go Security Team posted a bulletin on October 30.
Starting in Go 1.24, the runtime turns on MPTCP on every TCP listening socket if the host kernel permits it. If you deploy Caddy in front of production apps like Dagango.com, or run Go backend microservices on modern Linux distros, your servers have been listening on MPTCP for months. You opted into an unpatched kernel vulnerability without touching a configuration file.
Inside the Race Condition
The kernel bug sits in IPv4 TCP handshake processing.
When a client initiates a connection, Linux allocates a request socket to track the half-open state. During this handshake, the kernel parses incoming IPv4 options (ireq_opt) from the TCP SYN packet to echo them in the SYN-ACK response.
Normal TCP socket teardown follows strict state transitions. MPTCP complicates this lifecycle because it binds multiple subflows across separate interfaces to one logical connection.
During rapid socket migration or teardown, the kernel freed the option buffer with a standard call:
kfree(ireq->opt);
That plain kfree() caused a classic Use-After-Free (UAF) race condition. While one Central Processing Unit (CPU) core freed the option buffer, another core was actively reading that memory to build the outbound SYN-ACK packet.
Upstream maintainer Eric Dumazet fixed the flaw in four lines across net/ipv4/af_inet.c and net/ipv4/tcp_ipv4.c. The fix replaced the immediate free with Read-Copy-Update (RCU) reclamation:
kfree_rcu(ireq->opt, rcu);
By switching to kfree_rcu(), the kernel ensures that the buffer remains valid until all active read-side critical sections complete.
Why the AI Exploit Changed the Threat Model
Use-after-free races in the network stack are notoriously difficult to weaponize over a wire. Lost races usually crash the target host and alert monitoring systems immediately.
Anthropic researcher Xinyang Ge showed why CVE-2026-98432 broke that rule.
Their research model revealed an operational property of this race:
- Lost race attempts do not panic the host kernel.
- Failed attempts simply drop the packet or return a clean TCP reset.
- The remote client receives no connection, but the server stays completely healthy.
This operational quirk gives an attacker infinite retries.
An exploit runner can spray forged IPv4 options across thousands of parallel handshakes. Because failed attempts cause zero downtime, the attacker can brute-force memory layouts until the race succeeds. Once the freed buffer holds controlled data, the attacker gains unauthenticated remote code execution as root.
On Hacker News, the discussion reached 438 points and 204 comments. The community realized this was a reliable remote root exploit against public listeners, not a theoretical flaw.
The Language Runtime Trap
Most infrastructure teams assumed they were immune because they never explicitly enabled MPTCP in their code.
Go 1.24 made that decision for you.
When your application calls net.Listen("tcp", ":443"), the Go runtime checks whether the underlying Linux kernel exposes MPTCP support. If the host kernel permits it, Go requests an MPTCP socket under the hood instead of a standard IPPROTO_TCP socket.
In standard production setups, this creates an invisible architectural shift:
- The developer writes standard Go HTTP server code.
- Go's net package requests
IPPROTO_MPTCPfrom the Linux kernel. - The Linux kernel exposes the full subflow handshake state machine to incoming packets.
- The host becomes vulnerable to remote memory corruption before application code handles the request.
You can theoretically override this behavior per binary by setting an environment variable:
export GODEBUG=multipathtcp=0
Relying on GODEBUG across dozens of containers and systemd units is a fragile defense. If a single developer forgets the environment flag on a new deployment, that service leaves the entire host exposed.
Security policy belongs in the operating system and the network firewall, not in application environment variables.
Distro Lag Leaves Fleets Exposed
Upstream Linux kernel maintainers moved fast. Patched kernels landed between October 11 and October 15 across stable branches:
- 5.10.272
- 6.1.190
- 6.6.159
- 6.12.113
- 7.2.10
Enterprise distributions tell a different story.
As of October 31, the Ubuntu security tracker lists active Long-Term Support (LTS) releases (including 22.04, 24.04, and 26.04) as "Vulnerable" with updates in progress. Red Hat escalated the issue to Critical in bulletin RHSB-2026-016, but many downstream distributions have not shipped updated packages.
Ubuntu hosts enable MPTCP by default out of the box:
$ sysctl net.mptcp.enabled
net.mptcp.enabled = 1
If you run Ubuntu and deploy services compiled with Go 1.24, your servers are currently exposed.
Auditing and Mitigating Your Hosts Today
You do not need to wait for upstream distribution packages. You can audit and protect your infrastructure right now.
First, check if any process on your host is listening on an active MPTCP socket:
ss -Mlnp
If that command returns listening sockets, your services actively expose MPTCP to the network.
To secure your fleet immediately without rebooting, apply a two-layer defense.
1. Disable Kernel MPTCP Globally
Turn off MPTCP at the kernel level across all network interfaces:
sudo sysctl -w net.mptcp.enabled=0
echo "net.mptcp.enabled = 0" | sudo tee /etc/sysctl.d/99-disable-mptcp.conf
This sysctl prevents new MPTCP sockets from binding.
However, existing sockets remain open in the kernel. You must restart your Go services, Caddy reverse proxies, and container daemons for the change to take full effect.
2. Drop Optioned SYN Packets via nftables
The remote exploit injects malicious IPv4 options during the initial handshake to spray controlled memory into the kernel.
Legitimate web traffic on modern networks almost never uses IPv4 options. Standard IPv4 headers are exactly 20 bytes long, meaning a header length of 5 in 32-bit words. Any length greater than 5 indicates optional fields.
Drop these packets in the prerouting table before connection tracking allocates any kernel state:
table inet filter {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
tcp flags syn ip hdrlength gt 5 counter drop
}
}
This single firewall rule drops malicious payloads at the network interface before they reach the TCP handshake engine.
Takeaway
Language runtimes should not make silent architectural decisions that expand kernel attack surfaces.
Go's goal to modernize transport protocols is understandable. But automatically opting every production HTTP listener into a complex kernel subsystem created an unvetted exposure vector across thousands of production servers.
Audit your sockets with ss -Mlnp today. Disable net.mptcp.enabled at the host level, drop optioned SYN packets at the perimeter, and never assume that a plain net.Listen() call only touches basic TCP.