How Denial of Service in SSH Key Exchange happens in Go golang.org/x/crypto and how to fix it
Introduction
In the cpdaemon service, we discovered a high-severity denial of service vulnerability stemming from its dependency on golang.org/x/crypto v0.32.0. This package provides the SSH implementation used by the daemon, and a flaw in the key exchange (kex) negotiation phase meant that a remote, unauthenticated attacker could exhaust server resources simply by initiating crafted SSH handshakes—no valid credentials required.
The vulnerable dependency was declared in cpdaemon/go.mod:
golang.org/x/crypto v0.32.0
This matters for any Go developer building services that accept SSH connections, tunnel traffic, or use SSH-based protocols for inter-service communication. The key exchange happens before authentication, meaning the attack surface is open to anyone who can reach the network port.
The Vulnerability Explained
What Happens During SSH Key Exchange?
When an SSH connection is established, the client and server perform a key exchange (kex) to agree on shared cryptographic secrets. This happens before any authentication. The process involves:
- Algorithm negotiation
- Diffie-Hellman (or similar) parameter exchange
- Key derivation
In golang.org/x/crypto/ssh v0.32.0 and earlier, the key exchange implementation did not properly limit the resources that could be consumed during this phase. An attacker could exploit this by:
- Initiating multiple concurrent SSH connections that enter the kex phase
- Sending specially crafted kex messages that force expensive computations
- Holding connections in the kex phase without completing the handshake
Attack Scenario Specific to cpdaemon
The cpdaemon service uses golang.org/x/crypto/ssh as a direct dependency (not just transitive—it's listed in the require block). An attacker who can reach the daemon's SSH listener could:
- Open hundreds of connections to
cpdaemon's SSH port - Begin the key exchange on each connection but never complete it
- Force the daemon to allocate unbounded resources (goroutines, memory for kex state, CPU for cryptographic operations) for each pending handshake
- Eventually exhaust the daemon's resources, causing it to become unresponsive to legitimate connections
This is particularly dangerous because:
- No authentication is needed—the attack happens during the pre-auth handshake
- The attacker doesn't need to know any credentials or keys
- A single attacker machine can potentially take down the service
The Vulnerable go.mod
require (
// ... other dependencies
golang.org/x/crypto v0.32.0 // VULNERABLE - CVE-2025-22869
golang.org/x/sync v0.12.0
// ...
)
The Fix
The fix is straightforward but critical: upgrade golang.org/x/crypto from v0.32.0 to v0.35.0, which contains the upstream patch that adds proper resource bounds during the SSH key exchange phase.
Before (vulnerable):
golang.org/x/crypto v0.32.0
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
After (fixed):
golang.org/x/crypto v0.35.0
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
Why Multiple Files Changed
-
cpdaemon/go.mod: Updated the declared dependency version from v0.32.0 to v0.35.0. Also updatedgolang.org/x/sys(v0.29.0 → v0.30.0) andgolang.org/x/text(v0.21.0 → v0.22.0) becausegolang.org/x/crypto v0.35.0requires these newer minimum versions of its sibling modules. -
cpdaemon/go.sum: Added the new checksums for v0.35.0 ofgolang.org/x/cryptoand the updated transitive dependencies. The Go module system usesgo.sumto verify download integrity, so these entries are essential.
What v0.35.0 Fixes Internally
The upstream fix in golang.org/x/crypto v0.35.0 adds proper resource controls during the SSH key exchange:
- Bounds on the number of concurrent key exchanges per connection
- Timeouts for incomplete kex negotiations
- Memory limits on kex message buffers
This ensures that an attacker cannot force unbounded resource allocation during the pre-authentication phase.
Prevention & Best Practices
-
Pin and regularly update dependencies: Use
go get -u golang.org/x/crypto@latestor tools like Dependabot/Renovate to stay current with security patches. -
Run vulnerability scanners in CI: Trivy,
govulncheck, and Snyk can catch known CVEs in your dependency tree before they reach production. -
Implement defense in depth for SSH services:
- SetServerConfig.MaxStartupsor equivalent connection limits
- Use firewall rules to limit connection rates to SSH ports
- Deploy SSH services behind a bastion or VPN when possible -
Monitor for resource exhaustion: Set alerts on goroutine counts, memory usage, and connection counts for services using
golang.org/x/crypto/ssh. -
Use
govulncheckfor Go-specific analysis:
bash go install golang.org/x/vuln/cmd/govulncheck@latest govulncheck ./...
This tool checks not just whether you depend on a vulnerable module, but whether your code actually calls the vulnerable function paths.
Key Takeaways
- Pre-auth attack surfaces are the most dangerous: CVE-2025-22869 requires zero credentials, making every exposed SSH port a potential target until patched.
cpdaemon/go.moddeclaredgolang.org/x/cryptoas a direct dependency, meaning the SSH functionality is core to the daemon—not an unused transitive dependency.- Transitive dependency bumps (
x/sys,x/text) are often required when upgrading Go standard library extensions, since they maintain minimum version compatibility across thegolang.org/xecosystem. - Trivy correctly identified the vulnerable version in
go.modwithout needing to trace call paths—version-based detection caught this before any exploit attempt. - A three-version jump (v0.32.0 → v0.35.0) was necessary because the fix landed in v0.35.0, not in a patch to the v0.32.x line—Go's
golang.org/xmodules don't backport security fixes.
How Orbis AppSec Detected This
- Source: Network connections reaching
cpdaemon's SSH listener, initiating the key exchange protocol - Sink: The
golang.org/x/crypto/sshkey exchange handler in the vulnerable v0.32.0 library, which processes unbounded kex messages without resource limits - Missing control: No resource bounds or timeouts on the SSH key exchange phase in
golang.org/x/cryptov0.32.0 - CWE: CWE-400 (Uncontrolled Resource Consumption)
- Fix: Upgraded
golang.org/x/cryptofrom v0.32.0 to v0.35.0 incpdaemon/go.mod, which includes upstream resource limiting during SSH key exchange
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2025-22869 is a reminder that cryptographic protocol implementations carry risk even in well-maintained libraries. The SSH key exchange phase—executed before any authentication—is a critical attack surface. By keeping golang.org/x/crypto updated and running continuous dependency scanning, you can ensure that your Go services aren't vulnerable to resource exhaustion attacks that require nothing more than a TCP connection to exploit.
For any service using golang.org/x/crypto/ssh, verify your version is at least v0.35.0 today. If you're still on v0.32.0 or earlier, you're exposed.