Safeguard
Vulnerability Analysis

CVE-2020-29652: Denial of service in golang.org/x/crypto/...

A pre-auth nil pointer dereference in golang.org/x/crypto/ssh let a single crafted request crash Go SSH servers. Here's the impact, fix, and remediation path.

Nayan Dey
Security Researcher
8 min read

A single malformed authentication request was all it took to crash a Go-based SSH server. CVE-2020-29652 is a denial-of-service vulnerability in golang.org/x/crypto/ssh, the SSH implementation used by a large share of Go's server and networking ecosystem, in which a remote, unauthenticated client could trigger a nil pointer dereference during the authentication phase of an SSH connection. Because an unrecovered panic in Go terminates the process by default, a single crafted connection was enough to crash the entire SSH-serving application — not just the offending session — making this a low-effort, high-impact availability bug for anyone exposing an x/crypto/ssh-based server to the network.

This post breaks down what the vulnerability is, who it affects, how it was fixed, and how a supply chain security program like Safeguard's is designed to catch issues like this before they reach production.

What the vulnerability is

golang.org/x/crypto/ssh is the extended Go standard library package that implements the SSH protocol — both client and server sides — for the Go ecosystem. It's not part of the Go core distribution, but it ships under the official golang.org/x umbrella and is treated as a de facto standard dependency for anything that speaks SSH in Go: Git hosting platforms, SFTP servers, container build and CI tooling, IoT management agents, and countless internal infrastructure tools.

CVE-2020-29652 is a nil pointer dereference (CWE-476) in the server-side authentication handling path. A remote client could send a specially crafted authentication request during the SSH handshake that caused the server's connection-handling code to dereference a pointer that had not been initialized, rather than following the normal authentication-success or authentication-failure branches. Because each incoming SSH connection is typically handled in its own goroutine, and Go's runtime terminates the whole process on an unrecovered panic unless the application explicitly recovers it, the result was a full crash of the listening SSH service — not just a dropped connection. No valid credentials, prior authentication, or special network position were required to trigger it; the attacker only needed the ability to open a TCP connection to the affected server and speak enough of the SSH handshake to reach the vulnerable code path.

The practical effect: any Go service accepting untrusted inbound SSH connections and built on a vulnerable version of x/crypto/ssh could be knocked offline by a single crafted request, repeatably, until the underlying dependency was patched.

Affected versions and components

The vulnerability lives in the SSH server code inside the golang.org/x/crypto module, specifically the ssh subpackage (golang.org/x/crypto/ssh). Because x/crypto is versioned as a Go module with pseudo-versions rather than traditional semantic version tags, "affected versions" is best expressed as "all module versions/commits prior to the fix," which landed in mid-December 2020.

Any application that:

  • imports golang.org/x/crypto/ssh directly or transitively, and
  • uses it to run an SSH (or SSH-based, e.g. SFTP) server exposed to untrusted clients

was potentially exposed. This includes self-hosted Git servers, SFTP/file-transfer gateways, custom bastion or jump-host implementations, and any bespoke remote-access tooling written in Go. Pure SSH-client usage (e.g., a Go program that only dials out to other SSH servers) is a much smaller concern here, since the flaw sits in the code path that processes inbound authentication attempts on the server side.

Because x/crypto/ssh is frequently pulled in as a transitive dependency of higher-level libraries and platforms, many affected organizations wouldn't have seen "x/crypto" anywhere in their own go.mod diff history — the vulnerable code arrived through two or three layers of indirection, which is exactly the pattern that makes dependency-level vulnerabilities hard to track by memory or manual review alone.

CVSS, EPSS, and KEV context

NVD's scoring for CVE-2020-29652 reflects a network-exploitable, pre-authentication denial-of-service condition: CVSS 3.1 base score of 7.5 (High), with a vector consistent with AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H — network attack vector, low attack complexity, no privileges or user interaction required, no confidentiality or integrity impact, but high availability impact. That combination is typical of crash-the-process bugs: nothing is stolen or altered, but the service goes down, and it goes down easily.

From an exploitation-likelihood standpoint, this CVE has not attracted the kind of active, opportunistic scanning and exploitation that drives EPSS scores upward, and it does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog. That's consistent with the nature of the bug: it requires a target running a specific Go dependency in a specific server role, and denial-of-service bugs in general are less attractive to attackers seeking persistence or data access than they are to attackers seeking pure disruption. Low observed-exploitation signals are useful context, but they shouldn't be read as "safe to ignore" — a pre-auth, network-reachable crash bug in a widely embedded SSH library is exactly the kind of low-cost, high-annoyance tool that gets used opportunistically once public awareness of a flaw exists, particularly against exposed internal tooling that lags behind on patching.

Timeline

  • Discovery and fix development: The issue was identified in the SSH server authentication handling of golang.org/x/crypto/ssh and addressed by the Go security team and package maintainers.
  • December 2020: A fix was merged into the golang.org/x/crypto module, closing the nil pointer dereference path in the server authentication code.
  • Public disclosure: The issue was assigned CVE-2020-29652 and published to the National Vulnerability Database, alongside a corresponding Go vulnerability database / GitHub Security Advisory entry, so that dependency scanners and go list -m / govulncheck-style tooling could flag affected module versions.

As with most x/crypto fixes, there was no dedicated point-release "recall" mechanism the way there is for a versioned library with tagged releases — consumers needed to update to a module version (pseudo-version) built from a commit at or after the fix, which meant the fix's real-world impact depended heavily on how promptly downstream projects picked up a fresh go.sum.

Remediation steps

  1. Update the dependency. Run go get -u golang.org/x/crypto (or pin explicitly to a post-fix pseudo-version/commit) in any module that imports golang.org/x/crypto/ssh, then regenerate go.sum and rebuild. Confirm the resolved version in go.mod actually reflects a post-fix commit rather than an older cached pseudo-version.
  2. Check transitive dependencies, not just direct ones. Use go mod graph or go mod why golang.org/x/crypto to find out whether the vulnerable package is being pulled in indirectly through another library (Git server frameworks, SFTP libraries, deployment agents, etc.), and confirm that upstream project has also picked up the fix.
  3. Rebuild and redeploy every affected binary. Because Go compiles dependencies statically into the final binary, updating go.sum alone doesn't help — every service binary that embeds the vulnerable code must be rebuilt and redeployed. Container images and artifacts built before the fix remain vulnerable indefinitely if they're not rebuilt.
  4. Add crash resilience as defense-in-depth. For SSH-serving applications generally, consider isolating connection-handling goroutines with recovery middleware so a single malformed request degrades gracefully (dropped connection) rather than taking down the whole process — this doesn't fix the underlying bug but limits blast radius for similar classes of issues.
  5. Run govulncheck (or an equivalent SCA tool) against your Go modules and built binaries to confirm no build artifacts still reference the vulnerable code path, since go.mod version pins don't always match what's actually compiled into an older artifact still in deployment.

How Safeguard Helps

CVE-2020-29652 is a textbook example of why dependency visibility has to extend past go.mod: the vulnerable package is routinely pulled in transitively, the fix required a full rebuild rather than a config change, and the affected component (a networked SSH server) is often the kind of internal tooling that doesn't get the same patch cadence as customer-facing infrastructure.

Safeguard's software supply chain security platform is built to close exactly these gaps:

  • Continuous SBOM-based dependency tracking identifies every module — direct or transitive — that resolves to a vulnerable version of golang.org/x/crypto, across every repository and build artifact, not just the ones where the import appears in a top-level go.mod.
  • Build-artifact correlation ties source-level fixes back to what's actually compiled and running, so a go.sum update that hasn't been followed by a rebuild and redeploy doesn't get silently marked as "resolved."
  • CVE and EPSS-aware prioritization surfaces network-exploitable, pre-auth issues like this one ahead of lower-severity findings, so DoS-class bugs in internet- or intranet-facing services get triaged with the urgency their exposure warrants, even when public exploitation signals are quiet.
  • Policy-driven gating can block builds or deployments that still reference known-vulnerable dependency versions, catching the "forgot to rebuild the container image" failure mode that turns a five-minute go get fix into a lingering production risk.

For teams maintaining Go-based network services, the lesson from CVE-2020-29652 isn't just "patch this one library" — it's that supply chain visibility needs to reach every transitive dependency, every build artifact, and every environment where an old binary might still be quietly listening for connections.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.