Originally published on ImportStatic.

Turning off Multipath TCP in a Go server removes the condition the published exploit for CVE-2026-98432 needs, but it leaves the kernel bug in place. Anthropic described that exploit on October 27 as zero-click remote code execution against Linux servers with an MPTCP listener, and since Go 1.24 a Go server on Linux has opened its listening sockets with MPTCP unless you told it otherwise. The Go Security Team's notice followed on October 30, and a Hacker News thread took up whether turning MPTCP off in Go protects you.

The bug is in the kernel's IPv4 TCP code, and the Go team's post warns that systems without MPTCP listeners may be open to similar attacks. Red Hat's bulletin treats any vulnerable kernel with IPv4 TCP as affected, and systems taking inbound TCP as the most exposed. A patched kernel is the fix. The steps below are for machines that can't have one yet, and as of November 1, 2026 that includes Ubuntu and RHEL.

We couldn't run Linux here, so the Go results below come from running the same code on macOS and inspecting binaries cross-built for Linux. ListenConfig.MultipathTCP() reports configuration and doesn't check the operating system, so it gives the same answer on both. The Linux commands (ss, sysctl, nft) were not run.

Check the kernel first

The CVE record lists kernels from 4.4 on as affected, fixed upstream in 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10 and 7.3. Those numbers only apply to upstream kernels, because distributions backport fixes under their own version strings. Check your vendor's tracker rather than comparing uname -r against that list.

Debian has fixed packages (6.12.113-1 for trixie, 6.1.190-1 for bookworm). Ubuntu lists its releases as vulnerable, with fixes in progress for 22.04 and later, and Red Hat's bulletin says fixes are being expedited but names no fixed packages. A new kernel does nothing until the machine reboots into it.

How the default is set

http.ListenAndServe calls net.Listen, which calls Listen on a zero ListenConfig, so ordinary server code takes the default. That default comes from the multipathtcp GODEBUG setting, and when nothing sets it explicitly, the go line in the main module's go.mod decides, not the toolchain version alone.

We built a small probe with Go 1.27.3 under ten configurations and printed ListenConfig.MultipathTCP():

Configuration Default listener uses MPTCP
go 1.20 no
go 1.23.0 no
go 1.24.0 yes
go 1.27.3 yes
go 1.27.3 plus godebug multipathtcp=0 in go.mod no
go 1.27.3 plus godebug default=go1.23 in go.mod no
go 1.27.3 plus //go:debug multipathtcp=0 in package main no
go 1.27.3 with GODEBUG=multipathtcp=0 in the environment no
go.mod sets multipathtcp=0, environment sets GODEBUG=multipathtcp=2 yes
go 1.27.3 with GODEBUG=multipath=0 (wrong key) yes

The environment wins over go.mod, so a value left in a base image or a systemd unit overrides what you compiled in.

A misspelt key in the environment has no effect. The GODEBUG docs say unrecognized keys in the variable are ignored, and the program ran with MPTCP on and printed no warning. The same misspelling in go.mod stops the build with go.mod:5: unknown godebug "multipath", which is one reason to prefer that form.

A dependency can move a module from one side of the 1.24 line to the other. We started from a module on go 1.23.0 with MPTCP off and added one dependency:

$ go get golang.org/x/net@latest go: upgraded go 1.23.0 => 1.26.0 go: added golang.org/x/net v0.62.0 

After that, the same main.go reported MPTCP on, because the dependency needed the higher go line.

Read the setting from a built binary

For a deployed program you don't need the source. Build information records every GODEBUG default that differs from the toolchain's own, and go version -m prints it:

go version -m ./server | grep -o 'multipathtcp=[0-9]' 

A binary from the go 1.23.0 module prints multipathtcp=0, and so does one built with the //go:debug line. A binary with no explicit setting and a go line of 1.24 or later prints nothing, and that empty result means MPTCP is on for listeners, provided the toolchain is 1.24 or newer. The information survived -ldflags="-s -w" -trimpath in our test.

For a directory of binaries or a CI step, this standard-library program does the same and also handles old toolchains:

// gocheck reports, for each Go binary given, whether its net.Listen // defaults to Multipath TCP on Linux. It reads the build information // embedded in the file and does not run the program. package main import ( "debug/buildinfo" "fmt" "go/version" "os" "strings" ) func main() { for _, path := range os.Args[1:] { fmt.Printf("%s: %s\n", path, check(path)) } } func check(path string) string { info, err := buildinfo.ReadFile(path) if err != nil { return "not a Go binary: " + err.Error() } if version.Compare(version.Lang(info.GoVersion), "go1.24") < 0 { return info.GoVersion + ", MPTCP off (toolchain older than 1.24)" } setting := "" for _, s := range info.Settings { if s.Key != "DefaultGODEBUG" { continue } for kv := range strings.SplitSeq(s.Value, ",") { if v, ok := strings.CutPrefix(kv, "multipathtcp="); ok { setting = v } } } switch setting { case "", "1", "2": return info.GoVersion + ", listeners use MPTCP by default" default: return info.GoVersion + ", multipathtcp=" + setting + ", listeners do not" } } 

Run against four of the Linux binaries from the table, with paths shortened:

go1.23/probe-linux: go1.27.3, multipathtcp=0, listeners do not go1.24/probe-linux: go1.27.3, listeners use MPTCP by default gomod-godebug/probe-linux: go1.27.3, multipathtcp=0, listeners do not env-off/probe-linux: go1.27.3, listeners use MPTCP by default 

The env-off configuration sets GODEBUG=multipathtcp=0 at run time, and the file doesn't record that, so the last line reports MPTCP on. This check reads compiled-in defaults only. It can't see the environment a process runs in, or a SetMultipathTCP call in the code, so treat it as a way to find candidates. On a running Linux host, ask the kernel, with flags from the ss man page:

sudo ss -Mlnp 

-M limits the output to MPTCP sockets, so anything listed there is an MPTCP listener and -p names the process.

Disabling MPTCP

On a host you control, start with the system-wide switch, which covers every program at once:

sudo sysctl -w net.mptcp.enabled=0 echo 'net.mptcp.enabled = 0' | sudo tee /etc/sysctl.d/90-disable-mptcp.conf 

The kernel documentation describes this as controlling whether MPTCP sockets can be created, and calls it a per-namespace setting. It applies to new sockets, so restart the service, because a listener that's already open keeps running. Containers with their own network namespace need it set there too. A Go server still starts with the sysctl off, since listenMPTCP retries with plain TCP on any MPTCP error, and its comment names this sysctl.

When you don't own the host, set it per program. For a binary you can rebuild, add this to the main module's go.mod:

godebug multipathtcp=0 

Only the main module's file counts, so a line in a library has no effect, and in a workspace Go reads godebug lines from go.work instead. A go.mod line needs a rebuild but stays with the code. For a binary you can't rebuild, put GODEBUG=multipathtcp=0 in its environment.

A single listener can opt out in code:

var lc net.ListenConfig lc.SetMultipathTCP(false) ln, err := lc.Listen(ctx, "tcp", addr) 

SetMultipathTCP overrides GODEBUG in both directions. In our run with GODEBUG=multipathtcp=0 set, calling SetMultipathTCP(true) still gave true. A dependency that opens its own listener and asks for MPTCP keeps it whatever you set in go.mod, and the sysctl is the only switch here that reaches that case. The risk of missing one listener is what the Go team means when it calls the per-program approach "a fragile mitigation".

The default value, multipathtcp=2, enables MPTCP for listeners only, and our probe's default Dialer reported false in every configuration.

Side effects and remaining exposure

Clients that don't request MPTCP see no change, because per mptcp.dev a server already treats their connections as plain TCP. Clients that do request it fall back to plain TCP on one path. If your service relies on multipath for handover or aggregation, weigh that against how long your kernel fix is going to take. If it doesn't, we'd turn MPTCP off on any exposed host that's still waiting for a patched kernel.

For kernels that are still unpatched, Red Hat publishes an nftables rule that drops inbound IPv4 TCP packets carrying IP options. Red Hat says it reduces exposure but can't fully prevent the issue, and that you should test it first in case a workload needs IP options. It's worth applying alongside the MPTCP change on an exposed host, and removing once the kernel is fixed.

Issue 82717 proposes turning the Go default off and was still open with no decision on October 31, and the announcement calls a change premature. An explicit godebug multipathtcp=0 works today without waiting for that outcome. We tested other behaviour changes tied to the go line in the Go 1.27 upgrade.

Originally published on ImportStatic. This article was drafted with AI assistance; every claim was checked against the sources linked in it and the code samples were compiled and run.