CVE-2026-46598: the ssh/agent ed25519 panic in golang.org/x/crypto, and how to upgrade to v0.52.0
| Severity | Medium. CVSS 3.1 base 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L) |
|---|---|
| Actively exploited? | No. Not on CISA KEV; CISA SSVC Exploitation: none |
| Affected | golang.org/x/crypto < 0.52.0 (module path golang.org/x/crypto/ssh/agent) |
| Fixed in | golang.org/x/crypto v0.52.0 |
| Type (CWE) | CWE-129: Improper Validation of Array Index (manifests as a denial-of-service panic) |
Exploitation status
CVE-2026-46598 is not on the CISA Known Exploited Vulnerabilities catalog, and CISA's own SSVC decision record scores it Exploitation: none. The Go security team assigned it Go advisory GO-2026-5033 and a CVSS 3.1 base of 5.3 (Medium). So this is a real bug worth fixing, but it is not a five-alarm fire and nobody should be reading "Critical" anywhere on this page.
No public exploit or proof-of-concept beyond the upstream test case is referenced in the advisory. The interesting nuance is the SSVC "Automatable: yes" flag: because the trigger is a single malformed wire message rather than a multi-step chain, a script could spray it at many ServeAgent listeners cheaply. That raises the nuisance ceiling for an availability bug, even though the worst outcome is still just a crashed process.
What is CVE-2026-46598?
CVE-2026-46598 is a denial-of-service flaw in the Go module golang.org/x/crypto, specifically the ssh/agent package. For certain crafted inputs the code built an ed25519.PrivateKey by casting malformed wire bytes directly into the key type instead of validating the length first. When that under-sized slice is later used for a signing or marshalling operation, the standard library indexes past the end of it and the program panics. The CNA (the Go team) classifies it as CWE-129, improper validation of an array index.
In plain terms: feed the agent a key blob that claims to be an ed25519 key but is the wrong size, and the Go process handling it dies. There is no memory disclosure and no way to forge a signature; the entire impact is the crash. That is exactly what the CVSS vector says: A:L with confidentiality and integrity both NONE.
Why this CVE matters
It matters because of where golang.org/x/crypto/ssh/agent tends to live, not because of the score. This package backs SSH agent servers and agent-forwarding code inside Go programs. Think jump hosts, CI runners, bastion services, Teleport-style access proxies, and anything that calls ServeAgent to expose an agent over a socket. The reporter credit goes to NCC Group Cryptography Services sponsored by Teleport, which tells you the realistic target shape: a long-lived agent process that accepts key material from clients you do not fully trust.
The affected routines are parseEd25519Cert, parseEd25519Key, ForwardToAgent and ServeAgent. If your program calls ServeAgent and a client (or a forwarded upstream) can hand it ed25519 key bytes, an attacker can panic that goroutine and, depending on how you recover, take down the listener. A short-lived CLI that only signs with its own on-disk keys barely feels this. A multi-tenant agent endpoint feels it a lot.
Am I affected? Detect the vulnerable version
You are affected if any binary you ship or run imports golang.org/x/crypto at a version below 0.52.0. The vulnerability lives in the module, so the question is "which version of the dependency did this binary compile against", not "which OS am I on". Three concrete ways to check, from your repo or a built artifact:
# A. From a module's source tree. Show the resolved x/crypto version.
go list -m golang.org/x/crypto
# golang.org/x/crypto v0.51.0 <- anything < v0.52.0 is vulnerable
# B. Find every module in a multi-module workspace that pulls it in,
# and see WHY (which of your deps requires it).
go mod why golang.org/x/crypto
go mod graph | grep golang.org/x/crypto
# C. Inspect an already-built binary you cannot rebuild from source yet.
# Go stamps module versions into the binary; read them back:
go version -m ./your-binary | grep golang.org/x/crypto
# dep golang.org/x/crypto v0.51.0 h1:...
Option C is the one most people forget. If you inherited a binary from a vendor or an old CI artifact, go version -m reads the embedded build info without any source at all. If it prints a version below v0.52.0, that artifact is affected and needs a rebuild. You cannot hot-patch a compiled Go binary.
How to fix CVE-2026-46598
The fix is to upgrade the dependency to golang.org/x/crypto v0.52.0 and rebuild. There is no OS package, no MSI, no apt or dnf update that fixes this. The vulnerable code is statically linked into your Go binary, so you bump the module and recompile. Here is the exact sequence.
- In each Go module that depends on x/crypto, upgrade to v0.52.0.
- Run
go mod tidyso the upgrade propagates togo.modandgo.sum. - Rebuild every binary produced from that module. The old binary keeps the old, vulnerable copy of x/crypto until you recompile it.
- Redeploy the rebuilt binaries and restart the services so the patched code is actually running.
- Verify the embedded version, then confirm your agent listeners survive a malformed key (see verification below).
Upgrade the module to v0.52.0
# CVE-2026-46598: golang.org/x/crypto < 0.52.0 is affected. Fixed in v0.52.0.
# Go advisory: https://pkg.go.dev/vuln/GO-2026-5033
# 1. Show the current resolved version.
go list -m golang.org/x/crypto
# 2. Upgrade x/crypto to the patched release. Note the correct module path:
# it is golang.org/x/crypto, NOT a github.com/... mirror.
go get golang.org/x/[email protected]
# 3. Reconcile go.mod / go.sum and rebuild.
go mod tidy
go build ./...
# 4. Confirm the resolved version is now v0.52.0 or later.
go list -m golang.org/x/crypto
If a transitive dependency is the thing dragging in an old x/crypto, the go get golang.org/x/[email protected] still works, because Go's minimum-version selection will raise the whole graph to v0.52.0. You can prove it stuck with go mod why golang.org/x/crypto afterwards. Avoid the temptation to add a replace directive pointing at a fork; for a clean upstream release like this, a straight version bump is the maintainable path.
Confirm with govulncheck (the Go-native scanner)
The right scanner for a Go module flaw is govulncheck, not a generic host scanner. It reads the Go vulnerability database and, crucially, does call-graph analysis, so it will tell you whether your code actually reaches the vulnerable functions, not just whether the module is present.
# Install (or update) the official Go vulnerability scanner.
go install golang.org/x/vuln/cmd/govulncheck@latest
# Scan your module's source. Before the fix this reports GO-2026-5033.
govulncheck ./...
# Scan an already-built binary instead of source:
govulncheck -mode=binary ./your-binary
Before you upgrade, expect govulncheck to print GO-2026-5033 with a trace into parseEd25519Key / ServeAgent if your code is actually exposed. After the upgrade and rebuild, that finding should be gone. If govulncheck reports the module is present but no symbol is reachable, your exposure is lower, but still rebuild, because reachability analysis can miss reflection and dynamic dispatch.
Verify the fix actually landed
# CVE-2026-46598 verification checklist.
# 1. The rebuilt binary embeds v0.52.0 (or later):
go version -m ./your-binary | grep golang.org/x/crypto
# expect: dep golang.org/x/crypto v0.52.0 ...
# 2. govulncheck no longer reports GO-2026-5033:
govulncheck -mode=binary ./your-binary
# 3. Functional check: confirm the agent listener no longer dies on a bad key.
# Restart the service, then watch for panic stack traces while you exercise it.
journalctl -u your-go-service --since "10 minutes ago" | grep -i "panic\|ed25519"
The single most common mistake here is upgrading go.mod but redeploying a stale binary. The module version in source means nothing until you recompile and ship; go version -m on the artifact that is actually running in production is the only check that proves it.
If you cannot rebuild immediately
There is no upstream config flag that disables the bad code path, so the only real mitigations are about reducing who can reach the parsing routines. Ranked by how much they actually help:
- Restrict the agent endpoint. If you run
ServeAgentover a network listener, put it behind authentication and an allow-list so only trusted clients can send key material. A Unix-domain socket with tight file permissions is far safer than a TCP listener. - Be careful with agent forwarding.
ForwardToAgentexposes you to whatever the upstream side sends. Disable forwarding to or from hosts you do not control until the rebuild ships. - Add panic recovery at the goroutine boundary as a stopgap only. A
recover()around the per-connection handler stops one malformed key from taking down the whole listener, but it does not fix the underlying CWE-129 and it can mask other failures. Treat it as a bandage you remove after upgrading. - Do not rely on a WAF. The malformed key rides inside the SSH agent protocol, which a web firewall cannot parse. Network filters that only understand HTTP give you nothing here.
Full fix path in order
- Inventory every Go binary that imports
golang.org/x/cryptobelow v0.52.0 (usego version -mon artifacts you cannot rebuild from source). - Bump the module to v0.52.0 in each affected repo with
go get golang.org/x/[email protected], thengo mod tidy. - Rebuild and redeploy: the fix only exists in recompiled binaries.
- Run
govulncheckin source mode and binary mode and confirm GO-2026-5033 is cleared. - For any agent endpoint that was reachable by untrusted clients, review logs for repeated panic-and-restart cycles, which would indicate someone was already probing it.
Because the only impact is availability, there is no credential-rotation or data-breach cleanup to do here. If you saw crash loops on an exposed agent listener before patching, that is worth a note in your incident log, but a panic alone does not imply key compromise.
Frequently asked questions
What is the fixed version for CVE-2026-46598?
golang.org/x/crypto v0.52.0 fixes it. Every version below 0.52.0 is affected. Run go get golang.org/x/[email protected], then go mod tidy, and rebuild every binary that imports golang.org/x/crypto/ssh/agent.
Is CVE-2026-46598 a critical, exploitable vulnerability?
No. It is rated Medium, CVSS 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L). The only impact is availability: a crafted ed25519 wire key makes the parsing code panic and crash the process. There is no confidentiality or integrity impact, CISA's SSVC rates Exploitation as none, and it is not on the CISA KEV catalog.
Which code paths in golang.org/x/crypto are affected?
The named routines are parseEd25519Cert, parseEd25519Key, ForwardToAgent and ServeAgent. A program that runs an agent server (ServeAgent) or forwards to an agent (ForwardToAgent) and parses attacker-supplied ed25519 key bytes can be crashed. A CLI that only uses its own local keys is far less exposed.
Can a WAF or firewall mitigate CVE-2026-46598?
Not meaningfully. The vulnerable data travels inside the SSH agent protocol channel, not as inspectable HTTP, so a web firewall never sees it. If you cannot rebuild immediately, restrict which clients can reach the agent socket or ServeAgent listener. The durable fix is upgrading the module and recompiling.
Related fixes
Other flaws in this area worth reviewing while you patch this one:
- How to Fix CVE-2026-28684: Cwe-59: improper link resolution before file in python-dotenv
- How to Fix CVE-2026-25725: Critical Vulnerability in claude-code
- How to Fix CVE-2026-4995: Critical Vulnerability in OpenUI
- How to Fix CVE-2026-23530: Path Traversal in FreeRDP
- How to Fix CVE-2026-21496: Input Validation Flaw in iccDEV
References
- Go vulnerability database (canonical advisory): GO-2026-5033 (pkg.go.dev/vuln/GO-2026-5033)
- Upstream issue: go.dev/issue/79596
- Fixing change (Gerrit CL): go.dev/cl/781360
- golang-announce thread: groups.google.com/g/golang-announce
- MITRE CVE record: cve.org/CVERecord?id=CVE-2026-46598
This guide was assembled from the Go security team's CVE record and the GO-2026-5033 advisory. The CVSS 5.3 score and SSVC "Exploitation: none" reading come from the CISA ADP vulnrichment in that record. Always confirm against the upstream advisory before applying changes in production.
What an attacker actually needs (precondition breakdown)
Reading the CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L line by line tells you exactly how exposed you are, and it is more useful than the single number:
- AV:N (network). The malformed key can arrive over a network connection to your agent listener, not just from a local user. That is why this is worth taking seriously on a
ServeAgentendpoint. - AC:L, PR:N, UI:N. No special conditions, no privileges, no user interaction. If a client can talk to the agent and send key bytes, it can trigger the panic. This is why SSVC marks it Automatable: yes.
- S:U (scope unchanged). The blast radius is the vulnerable process itself; it does not pivot into other security authorities.
- C:N / I:N / A:L. The whole impact is the bottom line here: no data read, no data tampered, only a low availability hit (one crashed goroutine or process). That is the ceiling. Anyone telling you this is a confidentiality or RCE bug is wrong.
So the honest risk statement is: an unauthenticated client that can reach a Go SSH agent server built on x/crypto below v0.52.0 can crash that process by sending a wrong-sized ed25519 key. No more, no less. Plan your patch window around an availability bug, not a breach.