Back to Blog
high SEVERITY5 min read

How Denial of Service in SSH Key Exchange happens in Go golang.org/x/crypto and how to fix it

A high-severity denial of service vulnerability (CVE-2025-22869) was discovered in the SSH key exchange implementation of Go's `golang.org/x/crypto` library. The `cpdaemon` service depended on the vulnerable version v0.32.0, which could allow an attacker to exhaust server resources during the SSH handshake phase. The fix upgrades the dependency to v0.35.0, which includes the upstream patch for this vulnerability.

O
By Orbis AppSec
Published June 10, 2026Reviewed June 10, 2026

Answer Summary

CVE-2025-22869 is a high-severity Denial of Service vulnerability in Go's `golang.org/x/crypto/ssh` package (CWE-400) that allows attackers to consume excessive resources during the SSH key exchange handshake. The fix is to upgrade `golang.org/x/crypto` from v0.32.0 to v0.35.0 in your `go.mod` file, which includes the upstream patch that adds proper resource limits during the key exchange phase.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade golang.org/x/crypto from v0.32.0 to v0.35.0
riskRemote attacker can crash or exhaust resources of any service using golang.org/x/crypto/ssh for SSH connections
languageGo
root causeInsufficient resource limits during the SSH key exchange phase in golang.org/x/crypto/ssh v0.32.0 and earlier
vulnerabilityDenial of Service in SSH Key Exchange

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:

  1. Algorithm negotiation
  2. Diffie-Hellman (or similar) parameter exchange
  3. 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:

  1. Open hundreds of connections to cpdaemon's SSH port
  2. Begin the key exchange on each connection but never complete it
  3. Force the daemon to allocate unbounded resources (goroutines, memory for kex state, CPU for cryptographic operations) for each pending handshake
  4. 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

  1. cpdaemon/go.mod: Updated the declared dependency version from v0.32.0 to v0.35.0. Also updated golang.org/x/sys (v0.29.0 → v0.30.0) and golang.org/x/text (v0.21.0 → v0.22.0) because golang.org/x/crypto v0.35.0 requires these newer minimum versions of its sibling modules.

  2. cpdaemon/go.sum: Added the new checksums for v0.35.0 of golang.org/x/crypto and the updated transitive dependencies. The Go module system uses go.sum to 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

  1. Pin and regularly update dependencies: Use go get -u golang.org/x/crypto@latest or tools like Dependabot/Renovate to stay current with security patches.

  2. Run vulnerability scanners in CI: Trivy, govulncheck, and Snyk can catch known CVEs in your dependency tree before they reach production.

  3. Implement defense in depth for SSH services:
    - Set ServerConfig.MaxStartups or equivalent connection limits
    - Use firewall rules to limit connection rates to SSH ports
    - Deploy SSH services behind a bastion or VPN when possible

  4. Monitor for resource exhaustion: Set alerts on goroutine counts, memory usage, and connection counts for services using golang.org/x/crypto/ssh.

  5. Use govulncheck for 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.mod declared golang.org/x/crypto as 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 the golang.org/x ecosystem.
  • Trivy correctly identified the vulnerable version in go.mod without 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/x modules 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/ssh key 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/crypto v0.32.0
  • CWE: CWE-400 (Uncontrolled Resource Consumption)
  • Fix: Upgraded golang.org/x/crypto from v0.32.0 to v0.35.0 in cpdaemon/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.

References

Frequently Asked Questions

What is a Denial of Service in SSH Key Exchange?

It's a vulnerability where an attacker initiates SSH connections and exploits the key exchange (kex) phase to consume excessive CPU, memory, or connection resources on the server, making it unavailable to legitimate users without needing valid credentials.

How do you prevent SSH Key Exchange DoS in Go?

Keep `golang.org/x/crypto` updated to at least v0.35.0, implement connection rate limiting, set SSH handshake timeouts, and use tools like Trivy to continuously scan dependencies for known CVEs.

What CWE is SSH Key Exchange DoS?

CWE-400 (Uncontrolled Resource Consumption), which covers scenarios where a system doesn't properly limit the resources allocated to processing requests, allowing attackers to cause resource exhaustion.

Is rate limiting enough to prevent SSH Key Exchange DoS?

Rate limiting helps reduce exposure but is not sufficient alone. The underlying library must properly bound resources during key exchange. Without the patched library, a single slow connection could still tie up resources disproportionately.

Can static analysis detect SSH Key Exchange DoS?

Yes, dependency scanners like Trivy, Snyk, and Govulncheck can detect known vulnerable versions of libraries. Trivy specifically flagged this CVE in the `cpdaemon/go.mod` file by matching the declared dependency version against the vulnerability database.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #210

Related Articles

critical

How buffer overflow in memcpy() happens in C/C++ embedded firmware and how to fix it

A critical buffer overflow vulnerability was discovered in the ESP32-based micro-journal firmware where `memcpy()` calls used `strlen()` without bounds checking, allowing oversized USB descriptor strings to corrupt adjacent memory. The fix replaces unbounded `strlen()` with `strnlen()` calls that enforce the destination buffer sizes (8, 16, and 4 bytes respectively), preventing heap/stack corruption from malicious USB devices.

high

How Denial of Service via crafted URI templates happens in Ruby addressable and how to fix it

A high-severity Denial of Service vulnerability (CVE-2026-35611) was discovered in the Ruby `addressable` gem versions prior to 2.9.0, which could allow attackers to crash or hang applications by sending specially crafted URI templates. The fix upgrades the dependency from version 2.8.7 to 2.9.0 across the Gemfile, Gemfile.lock, and gemspec in a Fastlane project, eliminating the vulnerable code path entirely.

critical

How Server-Side Request Forgery (SSRF) happens in Python requests.get() and how to fix it

A critical Server-Side Request Forgery (SSRF) vulnerability was discovered in `models/common.py` where `requests.get()` fetched images from arbitrary URLs without validating whether the target resolved to internal infrastructure. An attacker could supply URLs targeting AWS metadata endpoints (169.254.169.254), private networks, or localhost services through the Flask REST API. The fix introduces DNS-resolution-based validation using Python's `socket.getaddrinfo()` and `ipaddress` module to block

critical

How command injection happens in Python subprocess and how to fix it

A critical command injection vulnerability was discovered in export.py where subprocess calls used `shell=True` with user-controllable CLI arguments. An attacker could inject shell metacharacters through model paths or export parameters to execute arbitrary commands on the host system. The fix replaces shell-based command execution with safer list-based subprocess calls that prevent command injection.

critical

How integer overflow in path_join() happens in C and how to fix it

A critical integer overflow vulnerability was discovered in the `__cstl_join` function in `opencstl/filesystem.h` that could allow attackers to trigger a heap buffer overflow by supplying crafted file path strings. The fix adds an explicit overflow check before the size calculation, returning NULL when the combined path lengths would wrap around the `size_type64` maximum value.

critical

How stack buffer overflow happens in C memcpy() with caller-controlled length and how to fix it

A critical stack buffer overflow vulnerability was discovered in GDI/Comdlg32.cpp where the `memcpy()` function used a caller-controlled `lStructSize` field without validation, allowing attackers to write beyond stack-allocated buffers. The fix applies a simple `min()` check across four affected dialog functions to ensure copy operations never exceed the destination buffer size.