CVE
CVE-2026-98432
CWE
CWE-416, CWE-362, CWE-787
Affected Surface
- Linux kernel versions `4.4` (commit `079096f103fa`) through `< 5.10.272`, `< 5.15.223`, `< 6.1.190`, `< 6.6.159`, `< 6.12.113`, `< 6.18.56`, `< 7.2.10`, and `< 7.3-rc7` (`CVE-2026-98432`, CVSS 9.8 Critical)
- Go `>= 1.24` (`1.24.x`, `1.25.x`, `1.26.x`) network services running on unpatched Linux kernels, because Go 1.24+ sets `GODEBUG=multipathtcp=2` by default on `net.Listen` sockets (`golang/go#82717`)
TL;DR
On 29 and 30 October 2026, the Go Security Team issued a security advisory (oss-security 2026/10/30/1 and golang/go#82717) warning that every Go 1.24+ server on Linux enables Multipath TCP (MPTCP) by default on listening sockets, matching the exact precondition required by a newly disclosed zero-click Linux kernel remote root exploit for CVE-2026-98432 (CVSS 9.8 Critical):
- The kernel bug (
CVE-2026-98432): Introduced in Linux4.4(079096f103fa),tcp_v4_syn_recv_sock()transfers ownership of the IPv4 header options pointerireq->ireq_optfrom the half-open request socket to the newly created child socket (newinet->inet_opt) without copying it. While one CPU closes and destroys the child socket viainet_sock_destruct()(or migrates and destroys a cloned request socket viatcp_v4_reqsk_destructor()), another CPU processing a retransmittedSYNreadsireq->ireq_optunderrcu_read_lock()intcp_v4_send_synack()->ip_build_and_send_pkt()->ip_options_build(). Because both destructors freedinet_opt/ireq_optimmediately withkfree()instead of waiting for an RCU grace period viakfree_rcu(),ip_options_build()readsopt->optlentwice from freed kernel heap memory. - Zero-click remote root exploitation on MPTCP listeners: On 27 October 2026, Anthropic’s Frontier Red Team disclosed that its researcher (
Xinyang Ge) used an internal model to turnCVE-2026-98432into a two-stage unauthenticated remote root exploit against any Linux host with an exposed MPTCP listener. Because losing the SYN/close race only fails the TCP handshake without panicking the kernel on carefully chosen lengths, an attacker can retry continuously, leaking a kernel heap pointer in ~48 minutes to defeat KASLR and then overflowing the packet buffer in ~5 minutes to overwrite a callback pointer and execute a shell command asroot. - Why Go
1.24+services are uniquely exposed: In Go1.24(commit3d4daa38a7), the Go standard library changed the defaultGODEBUGsetting tomultipathtcp="2"(enable MPTCP on listeners only by default). Any HTTP server, API gateway (such as KrakenD, Caddy, Traefik, or a custom microservice), or gRPC server compiled with Go1.24,1.25, or1.26opensmptcplistening sockets on Linux even if no client ever negotiates MPTCP.
What to do now:
- Upgrade and reboot into a patched Linux kernel (
>= 5.10.272,>= 5.15.223,>= 6.1.190,>= 6.6.159,>= 6.12.113,>= 6.18.56,>= 7.2.10, or>= 7.3). - Immediately apply stopgap mitigations on unpatched hosts: Disable MPTCP system-wide with
sudo sysctl -w net.mptcp.enabled=0and start Go1.24+services withGODEBUG=multipathtcp=0(or addgodebug multipathtcp=0togo.mod). Note that disabling MPTCP only removes the precondition used by the demonstrated exploit; the underlyinginet_optrace lives in generic IPv4 TCP handling, so kernel patching remains mandatory.
Affected systems and fixed versions
| Component | Identifier | Severity | Affected versions | Fixed version / Mitigation | Vulnerable path |
|---|---|---|---|---|---|
Linux kernel (net/ipv4) | CVE-2026-98432 | CVSS 9.8 Critical (Kernel CNA) / 8.1 High (Red Hat) | 4.4 through < 5.10.272, < 5.15.223, < 6.1.190, < 6.6.159, < 6.12.113, < 6.18.56, < 7.2.10, < 7.3-rc7 | 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10, 7.3 | inet_sock_destruct() in net/ipv4/af_inet.c and tcp_v4_reqsk_destructor() in net/ipv4/tcp_ipv4.c |
Debian linux / linux-6.12 | CVE-2026-98432 (DSA-6568-1, DLA-4841-1, DLA-4850-1) | Critical | Debian 12 (< 6.1.190-1, < 6.12.113-1~deb12u1) and Debian 13 (< 6.12.113-1) | 6.1.190-1, 6.12.113-1~deb12u1, 6.12.113-1 | Backported commit 11a03ce3c4a6 |
Go standard library (net) | golang/go#82717 (oss-security 2026/10/30/1) | High exposure precondition | Go >= 1.24.0 on unpatched Linux kernels with net.mptcp.enabled=1 | Set GODEBUG=multipathtcp=0 at runtime or godebug multipathtcp=0 in go.mod | src/internal/godebugs/table.go (multipathtcp="2" default on net.Listen) |
Technical analysis
1. How inet_sock_destruct() frees inet_opt while tcp_v4_send_synack() is still reading it
When a Linux TCP listener receives an IPv4 SYN packet carrying IP header options (such as IPOPT_RR Record Route or IPOPT_TIMESTAMP), the kernel parses and stores those options in a struct ip_options_rcu pointed to by the half-open request socket (ireq->ireq_opt).
When the three-way handshake completes on the client’s ACK, tcp_v4_syn_recv_sock() allocates the full child socket (newinet) and transfers the ireq_opt pointer directly to newinet->inet_opt without copying the struct (xchg(&ireq->ireq_opt, NULL) or shared pointer during SYNACK retransmit).
Since Linux 4.4 (commit 079096f103fa, "tcp/dccp: install syn_recv requests into ehash table"), SYN_RECV request sockets are processed locklessly in the established hash table without holding the listener lock. That allows two CPUs to race on the same struct ip_options_rcu:
- CPU A (retransmitted
SYN): Processes a concurrent retransmittedSYNfor the same request socket insidetcp_check_req()->tcp_v4_send_synack(). Bothtcp_v4_send_synack()andinet_csk_route_req()dereferenceireq->ireq_optprotected only byrcu_read_lock(), and pass thatoptpointer toip_build_and_send_pkt(), which readsopt->optlento reserve skb header space and then callsip_options_build(), which readsopt->optlena second time tomemcpy()the options into the outgoingSYN-ACK. - CPU B (child socket teardown or request-socket migration): Immediately closes and destroys the child socket (which does not set
SOCK_RCU_FREE), callinginet_sock_destruct(), or destroys a migrated request-socket clone intcp_v4_reqsk_destructor().
Before Eric Dumazet’s fix (commit e3f33b0a1d89), both destructors freed the ip_options_rcu struct immediately with a plain kfree() while CPU A was still inside its rcu_read_lock() critical section:
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -161,7 +161,7 @@ void inet_sock_destruct(struct sock *sk)
WARN_ON_ONCE(sk->sk_wmem_queued);
WARN_ON_ONCE(sk->sk_forward_alloc);
- kfree(rcu_dereference_protected(inet->inet_opt, 1));
+ kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu);
dst_release(rcu_dereference_protected(sk->sk_dst_cache, 1));
dst_release(rcu_dereference_protected(sk->sk_rx_dst, 1));
psp_sk_assoc_free(sk);
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1209,7 +1209,7 @@ static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
*/
static void tcp_v4_reqsk_destructor(struct request_sock *req)
{
- kfree(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1));
+ kfree_rcu(rcu_dereference_protected(inet_rsk(req)->ireq_opt, 1), rcu);
}
2. Why the opt->optlen double-read yields both a KASLR leak and a heap overflow
When kfree() returns the ip_options_rcu object to its kmalloc slab while ip_build_and_send_pkt() is executing on another CPU, an attacker spraying another kernel object into that same slab slot between the two reads of opt->optlen gains two primitives:
- Stage 1 (Remote KASLR bypass via overread): If the reallocated object places a larger byte value at the
opt->optlenoffset afterip_build_and_send_pkt()checks it,ip_options_build()copies up to that largeroptlenfrom the freed/reallocated slab chunk directly into the outgoingSYN-ACKIP header sent back to the attacker over the network. As soon as one leakedSYN-ACKheader contains a kernel pointer from adjacent slab data, the attacker computes the kernel’s KASLR slide. - Stage 2 (Control-flow hijack via out-of-bounds write): Using the same
optlen/ offset mismatch inip_options_build(), the exploit overflows the buffer to overwrite a function callback pointer with the address of a ROP/function-call chain already staged in kernel memory via standard protocol primitives, executing an attacker-supplied shell command asroot.
In Anthropic’s proof-of-concept against a Linux server with an MPTCP listener, losing the race simply aborts the handshake without crashing the target, allowing continuous retries: Stage 1 leaked a kernel pointer in roughly 48 minutes, and Stage 2 achieved remote root command execution in another 5 minutes.
3. Why Go 1.24+ servers on Linux expose MPTCP listeners automatically
On Linux kernels where net.mptcp.enabled is 1 (the default on modern Debian, Ubuntu, Fedora, and RHEL derivatives), an application creates an MPTCP listening socket by passing IPPROTO_MPTCP (262) instead of IPPROTO_TCP (6) to socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP).
In Go 1.24 (commit 3d4daa38a7), src/internal/godebugs/table.go changed the default multipathtcp setting from "0" to "2" (listeners only):
// Go 1.24+ GODEBUG values for multipathtcp (doc/godebug.md):
// "0": disable MPTCP on dialers and listeners by default
// "1": enable MPTCP on dialers and listeners by default
// "2": enable MPTCP on listeners only by default (Go 1.24+ default)
// "3": enable MPTCP on dialers only by default
As a result, any Linux service compiled with Go 1.24, 1.25, or 1.26 that calls net.Listen("tcp", ...) or http.ListenAndServe(...) creates an IPPROTO_MPTCP listening socket by default. Ordinary TCP clients still connect over standard TCP, so operators rarely notice that their Go binaries are listening on MPTCP until they inspect ss -Mltn.
Detection and remediation
1. Audit Linux hosts for unpatched kernels and active MPTCP listeners
# 1. Check the running Linux kernel version and whether MPTCP socket creation is enabled
uname -r
sysctl net.mptcp.enabled 2>/dev/null || true
# 2. List all active Multipath TCP (MPTCP) listening sockets and owning PIDs (such as Go 1.24+ services)
ss -Mltnp 2>/dev/null || true
# 3. Check Go binaries on the host or in container images to see if they were built with Go >= 1.24
for bin in /usr/local/bin/* /opt/*/bin/*; do
[ -f "$bin" ] && go version -m "$bin" 2>/dev/null | head -n 2
done
2. Patch the kernel and apply immediate MPTCP / IP-options mitigations
- Upgrade the Linux kernel and reboot:
- Upstream stable fixes:
>= 5.10.272,>= 5.15.223,>= 6.1.190,>= 6.6.159,>= 6.12.113,>= 6.18.56,>= 7.2.10, or>= 7.3. - On Debian 12 (
bookworm) and Debian 13 (trixie), upgrade tolinux 6.1.190-1(DLA-4841-1) or6.12.113-1(DSA-6568-1/DLA-4850-1) and reboot the host (uname -r).
- Upstream stable fixes:
- Disable MPTCP socket creation at the kernel level (if MPTCP is not required):
Note that changingsudo sysctl -w net.mptcp.enabled=0 echo "net.mptcp.enabled = 0" | sudo tee /etc/sysctl.d/99-disable-mptcp.confnet.mptcp.enabledto0stops new MPTCP sockets from being created; restart any running Go services afterward and verify withss -Mltnpthat nomptcplisteners remain. - Disable default MPTCP listeners in Go
1.24+services:- At runtime (environment variable): Set
GODEBUG=multipathtcp=0in your systemd unit, Dockerfile, or Kubernetes Deployment manifest (ifGODEBUGis already set, append,multipathtcp=0). - At build time (
go.mod): Addgodebug multipathtcp=0to the main module’sgo.modfile:module example.com/myservice go 1.25.0 godebug multipathtcp=0 - Also check your codebase for explicit
ListenConfig.SetMultipathTCP(true)calls (rg -n 'SetMultipathTCP' .), which overrideGODEBUG.
- At runtime (environment variable): Set
- Drop inbound IPv4 packets carrying IP options at the perimeter firewall: Because
CVE-2026-98432requires aSYNpacket with non-empty IPv4 header options (ireq_opt/inet_opt, where IPv4ihl > 5), dropping inbound TCP packets with IPv4 options innftables(ip hdrlength > 20 tcp flags syn drop) or cloud security appliances blocks the vulnerableireq_optallocation path across all TCP and MPTCP listeners while kernel reboots are scheduled.
Timeline
- 2015-10-02: Linux
4.4merges commit079096f103fa("tcp/dccp: install syn_recv requests into ehash table"), introducing locklessSYN_RECVrequest-socket handling. - 2026-10-01: Eric Dumazet submits the patch
"ipv4: free inet_opt and ireq_opt after an RCU grace period"(e3f33b0a1d89), reported by Xinyang Ge of Anthropic. - 2026-10-15:
linux-cve-announcepublishesCVE-2026-98432as stable kernel backports (6.12.113,6.18.56,7.2.10) ship. - 2026-10-27: Anthropic’s Frontier Red Team publishes its research write-up demonstrating zero-click unauthenticated remote root code execution via
CVE-2026-98432against Linux servers with an MPTCP listener. - 2026-10-29 (
20:17to23:34 UTC) and 2026-10-30: Issuegolang/go#82717is opened and the Go Security Team publishes[security] Protecting against CVE-2026-98432ongolang-announceandoss-security(2026/10/30/1), detailingGODEBUG=multipathtcp=0andgodebug multipathtcp=0mitigations for Go1.24+servers.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Use AI-native static analysis to find similar application vulnerabilities and generate review-ready fixes.
References
- oss-security (2026/10/30/1): Go Security Team advisory on protecting against CVE-2026-98432 when using Go programs
- golang/go issue #82717: proposal: net: Disable MPTCP by default
- Linux kernel fix commit e3f33b0a1d89d738e2b913dab519226eaf76e15b (ipv4: free inet_opt and ireq_opt after an RCU grace period)
- linux-cve-announce: CVE-2026-98432 advisory and backport commits
- Anthropic Frontier Red Team: Discovering and exploiting vulnerabilities in critical, real-world software with Claude
- KrakenD advisory: CVE-2026-98432 Linux Kernel Flaw Exposing KrakenD on Unpatched Hosts