Jun 26, 2026·7 min read·2 visits
Unsolicited global or channel responses fill bounded internal Go channels, deadlocking the connection's read loop and leaking goroutines even after the connection is closed.
A denial-of-service (DoS) and resource leak vulnerability in the Go SSH package (golang.org/x/crypto/ssh) allows a malicious peer to permanently deadlock connection processing loops and leak memory. This issue stems from improper handling of unsolicited responses at the global and channel layers, which saturate internal bounded channel buffers and block the main multiplexer loop. The vulnerability is fully resolved in version 0.52.0.
The Go sub-repository golang.org/x/crypto/ssh is a widely utilized implementation of the SSH protocol in the Go ecosystem. It provides the core structures and multiplexing capabilities required to implement both SSH clients and servers. Because the SSH protocol permits multiple logical channels to run over a single physical transport layer, the package relies on an internal multiplexer layer to coordinate concurrent message streams.
CVE-2026-39830 is a design vulnerability within this multiplexer component. The issue stems from the improper handling of unexpected, unsolicited protocol responses. Specifically, both global-level and channel-level request response handlers lack safety gates to verify if a response was actually requested before placing it into internal Go channels.
An unauthenticated remote attacker can exploit this behavior to block the main connection-reading routine. By stalling the read loop, the attacker forces Go routines associated with the connection to hang indefinitely, even if the application subsequently attempts to close the underlying socket. This results in an incremental and permanent resource exhaustion of system memory and file descriptors.
The root cause of CVE-2026-39830 is a buffer saturation vulnerability that deadlocks the Go SSH connection multiplexer. The SSH multiplexer maintains a background packet processing routine named readLoop to deserialize incoming packets and route them to their respective channels or global handlers. When processing global request responses, the multiplexer routes success or failure packets into an internal Go channel named globalResponses, which has a bounded capacity of one.
Prior to the patch, the handleGlobalPacket function unconditionally dispatched received responses directly into the globalResponses channel. Because this channel is bounded, writing to it blocks the executing thread if the channel is already full. When a malicious peer sends an unsolicited global response packet, the channel becomes saturated. A subsequent unsolicited response packet blocks the handleGlobalPacket function indefinitely, which in turn blocks the entire readLoop goroutine.
Similarly, at the channel level, channel-specific response packets are processed by channel.handlePacket and written to the ch.msg channel, which has a bounded capacity of 16. If a malicious peer transmits unsolicited channel success or failure messages, the channel buffer overflows. The execution path blocks on the channel send statement, permanently halting the connection's main read routine.
This design flaw creates an un-killable deadlock state because the blocking operations occur on Go channels rather than network socket I/O. When the socket is closed by the server, the blocked readLoop goroutine never reaches the code path that handles socket closure. Consequently, the goroutines associated with the connection remain allocated in memory, creating a persistent leak.
The vulnerability was resolved through two targeted patches that introduce atomic state gates and non-blocking channel writes. In ssh/mux.go, the patch adds an atomic boolean flag named globalSentPending to track whether a global request is actively awaiting a response. If a response packet arrives while no request is pending, the message is discarded immediately instead of being pushed into the channel.
// ssh/mux.go patch highlights
case *globalRequestSuccessMsg, *globalRequestFailureMsg:
// Verify if a global request is actively pending
if !m.globalSentPending.Load() {
return nil // Safely discard unsolicited packet
}
select {
case m.globalResponses <- msg:
default:
// Non-blocking fallback to prevent lockups
}The second patch in ssh/channel.go duplicates this architecture at the channel layer by introducing sentRequestPending. Before executing a channel request, the package drains any spurious or residual messages from the channel buffer to ensure stale messages cannot interfere with valid operations. The message insertion is also wrapped in a non-blocking select statement.
// ssh/channel.go patch highlights
case *channelRequestSuccessMsg, *channelRequestFailureMsg:
// Verify if a channel request is pending
if !ch.sentRequestPending.Load() {
return nil // Discard unsolicited channel responses
}
select {
case ch.msg <- msg:
default:
// Fallback protects read loop if reader is slow
}These modifications successfully decouple the physical read loop from uncoordinated remote peer behavior. The implementation of atomic state gates ensures that unexpected packets are discarded at the boundary, preventing buffer saturation attacks. The transition to non-blocking writes ensures that even if a valid caller is delayed in reading, the core multiplexer thread remains unblocked.
Exploitation of CVE-2026-39830 does not require authentication or complex sequence timing. An attacker begins by establishing a standard TCP handshake with a target Go SSH service and completing the initial SSH key exchange protocol. Once the transport layer is established, the attacker can target either the global-level multiplexer or the channel-level multiplexer.
To target the global multiplexer, the attacker transmits two successive global response packets, such as SSH_MSG_GLOBAL_REQUEST_SUCCESS or SSH_MSG_GLOBAL_REQUEST_FAILURE. Because the target server has not initiated a global request, the first packet fills the single-slot globalResponses channel. The second packet causes the server's background read loop to block indefinitely while attempting to write to the full channel.
To target the channel-level multiplexer, the attacker first requests an interactive channel or subsystem. Once the channel is open, the attacker transmits 17 or more consecutive unsolicited SSH_MSG_CHANNEL_SUCCESS or SSH_MSG_CHANNEL_FAILURE packets. This overflows the 16-slot channel buffer on the server, resulting in an identical blocking condition in the background packet-reading thread.
The impact of CVE-2026-39830 is a complete denial of service and continuous resource exhaustion. Because the blocked routines are held on Go channel operations, calling Close() on the connection does not release the underlying structures. The main multiplexer read loop, the keep-alive loops, and the key exchange loops remain active in memory indefinitely.
An attacker can automate this sequence across thousands of concurrent connections to systematically exhaust the server's memory allocation. This leads to Out-Of-Memory (OOM) kernel interventions, which can terminate the host application or trigger system-wide deadlocks. Because no specialized authentication is required, the attack surface includes any public-facing application utilizing the vulnerable Go package.
The vulnerability is classified with a CVSS v3.1 score of 9.1, reflecting its high availability impact and low attack complexity. Although the vulnerability does not directly expose confidential system data, the resulting denial of service poses an operational risk to containerized environments and cloud-native gateways.
The primary remediation path is upgrading the golang.org/x/crypto package to version 0.52.0 or higher. This upgrade replaces the vulnerable multiplexer logic with the gated, non-blocking implementation. Developers should update their dependencies using standard Go module commands and rebuild affected binaries.
# Upgrade the crypto module to the patched version
go get golang.org/x/crypto@v0.52.0
go mod tidyIf immediate upgrading is not feasible, organizations should implement rate-limiting at the network layer to restrict the number of concurrent connections from single source IP addresses. Temporary connection limits can reduce the rate of resource depletion. However, because the leak is permanent, network-level mitigations only delay resource exhaustion and do not resolve the underlying vulnerability.
Security teams can identify ongoing exploitation by monitoring runtime metrics for abnormal trends. A continuous, linear increase in the total number of active goroutines that persists after client disconnects is a strong indicator of an active exploit. Running periodic pprof analyses can help locate blocked channel operations originating from the ssh package.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
golang.org/x/crypto/ssh Go | < 0.52.0 | 0.52.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-833 (Deadlock) |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 9.1 (Critical) |
| EPSS Score | 0.00392 |
| EPSS Percentile | 30.94% |
| Exploit Status | PoC (No Weaponized Public Exploits) |
| CISA KEV Status | Not Listed |
The program contains two or more threads or active execution paths that are permanently blocked waiting for each other or a shared resource, specifically a full Go channel.
A Denial of Service (DoS) vulnerability exists in the Go SSH implementation package (golang.org/x/crypto/ssh). The vulnerability is caused by a null pointer dereference (runtime panic) when CertChecker is utilized as a public key callback but its validation fields, IsUserAuthority or IsHostAuthority, are uninitialized.
An unbounded memory leak vulnerability in the Go SSH package (golang.org/x/crypto/ssh) allows authenticated users to crash the server by repeatedly requesting connection channels that are rejected, leading to system resource exhaustion.
A high-severity Denial of Service (DoS) vulnerability exists in the golang.org/x/crypto/ssh package prior to version 0.52.0. The vulnerability is caused by a lack of size and range validation on incoming RSA and DSA public key parameters during SSH authentication. An unauthenticated attacker can submit a crafted public key with pathologically large parameters, triggering intensive CPU computation during signature verification and leading to a complete Denial of Service.
An authentication bypass vulnerability was identified in the golang.org/x/crypto/ssh package. The library's verification logic for FIDO/U2F security keys failed to check the User Presence (UP) flag. This omission allows an attacker with access to a hardware token interface or an agent-forwarding socket to authenticate without physical user interaction.
A critical vulnerability exists in the Go SSH sub-repository (golang.org/x/crypto/ssh) before version 0.52.0. When an application writes payloads of 4GB or larger in a single write operation, integer truncation in the remote window calculation causes an infinite loop. This results in complete CPU core exhaustion and a denial-of-service condition.
An issue was discovered in Go's `golang.org/x/crypto/ssh/knownhosts` package where a revoked Certification Authority (CA) public key was not correctly checked for revocation during SSH host certificate validation. This allowed clients or servers utilizing the library to validate and trust host certificates issued by explicitly revoked CAs.