Jun 25, 2026·7 min read·4 visits
The Go SSH server implementation fails to enforce 'source-address' restrictions returned by 'VerifiedPublicKeyCallback', allowing remote clients to bypass IP-based network access controls.
An authorization bypass vulnerability exists in the golang.org/x/crypto/ssh package prior to version 0.52.0. When an SSH server is configured with a custom VerifiedPublicKeyCallback that returns a Permissions object containing a source-address critical option, the server fails to validate and enforce the restriction. This allows remote clients with valid public keys to bypass IP-based access restrictions and authenticate from unauthorized network locations.
The package golang.org/x/crypto/ssh is a supplemental Go library used to build custom SSH servers. It handles the cryptographic handshake, packet formatting, and protocol state transitions required by the SSH protocol. A core security component of this implementation is the authorization framework, which allows developers to configure custom authentication callbacks like PublicKeyCallback and VerifiedPublicKeyCallback within the ServerConfig struct.
During key authentication, the SSH protocol permits defining constraints on authorized keys using critical options. The source-address critical option restricts the network ranges or IP addresses from which a key can be used. This option forms a vital boundary for securing highly privileged credentials, ensuring they are only usable from trusted network perimeters.
CVE-2026-46595 exposes an incorrect authorization bug class (CWE-863) within the Go SSH server's authentication engine. When a server uses a custom VerifiedPublicKeyCallback and that callback returns a Permissions object containing a source-address critical option, the server fails to evaluate the restriction. This failure allows remote clients to bypass IP-based access restrictions and establish authenticated SSH sessions from unauthorized network locations.
The root cause of this vulnerability lies in the sequential state machine implemented within the serverAuthenticate function in ssh/server.go. Under normal SSH public key authentication (defined in RFC 4252), the client first sends an authentication query (SSH_MSG_USERAUTH_REQUEST with the isQuery boolean flag set to true) to verify if the server accepts a particular public key. The server responds by invoking the configured PublicKeyCallback, which returns a Permissions structure containing any active restrictions, such as a source-address requirement.
If the public key is acceptable, the client subsequently submits the cryptographic signature to prove possession of the corresponding private key. After verifying this signature, the server executes VerifiedPublicKeyCallback if defined. This second callback allows application logic to perform late-stage checks or modify the Permissions structure before concluding the authentication phase. This design permits developers to dynamically alter the permissions object returned during the initial query phase.
Prior to the patch, the code inside serverAuthenticate validated the source-address restriction only during the handling of the initial query phase in PublicKeyCallback. When the second phase concluded and VerifiedPublicKeyCallback returned its modified Permissions object, the server transitioned the state directly to authentication success without performing any subsequent network address validations on the newly returned object. This omission allowed any newly assigned or updated source-address critical option to be entirely ignored by the runtime environment.
To understand the execution path that leads to this vulnerability, it is necessary to examine the interaction between the protocol message handler and the callback pipeline. The SSH server's authentication engine processes incoming network packets sequentially, relying on local state transitions to track the progress of the handshake.
This architecture ensures that initial lookups do not require expensive cryptographic signature verifications, optimizing server resources. However, it introduces a temporal separation between the authorization query and the final establishment of the session. Because the state is modified dynamically during the second phase, any authorization checks performed in the first phase must be securely re-evaluated or continuous.
By treating the permissions object returned by VerifiedPublicKeyCallback as implicitly safe, the server created a classic state-synchronization gap. The network validation routine was decoupled from the final authorization boundary, allowing clients to successfully authenticate from prohibited IP ranges.
A detailed analysis of the patch applied to ssh/server.go reveals the precise location of the omission. In the vulnerable version, the invocation of VerifiedPublicKeyCallback received the current state permissions and returned a modified set of permissions, but the resulting object was never subjected to the checkSourceAddress routine.
// Vulnerable implementation snippet
if config.VerifiedPublicKeyCallback != nil {
perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, algo)
}
// Missing check here allowed authorization bypass
// The code immediately proceeded to transition state without validationThe fix, introduced in Gerrit CL 781642, inserts an explicit validation block immediately following the execution of VerifiedPublicKeyCallback. This change ensures that any permissions object returned from the callback containing critical options is evaluated for a source-address constraint.
// Patched implementation in ssh/server.go
if config.VerifiedPublicKeyCallback != nil {
perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, algo)
}
// Explicit enforcement of source-address option post-callback
if authErr == nil && perms != nil && perms.CriticalOptions != nil {
if saco := perms.CriticalOptions[sourceAddressCriticalOption]; saco != "" {
if err := checkSourceAddress(s.RemoteAddr(), saco); err != nil {
authErr = err
}
}
}This patch is highly localized and completely addresses the state synchronization discrepancy for the source-address critical option. Because sourceAddressCriticalOption is the primary built-in network restriction option supported natively by the package, verifying this specific index within CriticalOptions ensures that late-stage restrictions are fully enforced before authentication completes.
Exploitation of CVE-2026-46595 does not require complex cryptographic attacks or memory manipulation. Instead, it relies on the mismatch between the server's configuration assumptions and the actual execution path. An attacker must possess a valid private key that is configured on the target SSH server.
To initiate the attack, the adversary connects from an unauthorized network address (an IP not matching the whitelist defined in the key's permissions). The attacker's SSH client requests authentication using the public key. During the initial query stage, if the server configuration does not define strict limits at that stage or if the limits are dynamically assigned by the post-verification logic, the handshake continues.
Upon receiving the signature verification request, the attacker submits a valid signature. The server verifies the signature, executes VerifiedPublicKeyCallback, and receives a Permissions object containing the source-address restriction. Because the vulnerable server skips validation of this returned constraint, the authentication succeeds, granting the attacker shell access or command execution capabilities from an untrusted network location.
The security impact of this vulnerability is critical, as reflected by its CVSS base score of 10.0. Network-level source address restrictions are a key defense-in-depth control used to isolate sensitive management interfaces, administrative gateways, and automated file-transfer endpoints.
By bypassing this control, an attacker who has obtained a compromised key (e.g., via a leaked developer machine or an exposed backup) can access internal systems from any internet-connected host. This bypass circumvents IP whitelisting rules that are meant to restrict access to corporate VPNs, designated bastion hosts, or specific office IP ranges.
Furthermore, because SSH servers built with golang.org/x/crypto/ssh are frequently used in infrastructure automation, microservices, and Kubernetes ingress controllers, exploitation can lead to unauthorized control over critical orchestration planes. The vulnerability crosses security boundaries, transforming a constrained, network-restricted credential into an unrestricted administrative entry point.
Remediation of CVE-2026-46595 requires updating the golang.org/x/crypto module to version v0.52.0 or later. This can be achieved by running the command go get golang.org/x/crypto@v0.52.0 in the root of the project and rebuilding the affected binaries. This ensures that the post-callback network validation logic is active.
If immediate dependency updates are not possible, developers must implement manual IP verification logic inside their custom VerifiedPublicKeyCallback. By calling conn.RemoteAddr() and parsing the IP against the expected CIDR blocks, the callback can manually return an authorization error, effectively replicating the patch at the application layer.
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, perms *Permissions, algo string) (*Permissions, error) {
// Manual workaround enforcing source-address constraint
remoteIP := conn.RemoteAddr().String()
if !isAddressAllowed(remoteIP, "192.168.99.99") {
return nil, errors.New("ssh: unauthorized remote IP")
}
return perms, nil
}Security teams should also review historical SSH authentication logs. Look for successful authentications where the client IP does not align with the configured access policies of the corresponding user accounts, which may indicate that a restricted key was abused from an unauthorized network location.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
golang.org/x/crypto Go | < v0.52.0 | v0.52.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 |
| Attack Vector | Network |
| CVSS v3.1 Score | 10.0 |
| EPSS Score | 0.00385 |
| Impact | Critical (Authorization Bypass / Unauthorized Access) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The software performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly associate the results of the check with the actor's identity, or fails to execute the check in all required execution paths.
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.
A critical vulnerability exists in MessagePack-CSharp's typeless deserialization mechanism where configured blocklists fail to recursively inspect nested types. An attacker can bypass security restrictions by wrapping unauthorized types in arrays or generic collections, allowing insecure deserialization and remote code execution.
A critical prototype pollution vulnerability exists in the i18next-fs-backend Node.js package (prior to version 2.6.6) through its translation persistence layer. When handling missing translation keys, insecure traversal of JSON objects via the getLastOfPath function allows remote, unauthenticated attackers to mutate Object.prototype, potentially leading to denial of service, security bypasses, or remote code execution.
CVE-2026-48708 details a critical concurrency synchronization flaw in OliveTin versions < 3000.13.0. A shared package-level text/template.Template instance is accessed concurrently across multiple goroutines without proper synchronization. When concurrent request processing occurs, a race condition causes Go runtime panics or command contamination across separate sessions, enabling denial of service or execution of contaminated commands.
A missing authorization vulnerability in the OliveTin system allows unauthenticated remote actors to query the ValidateArgumentType RPC endpoint. By exploiting this flaw, attackers can execute systematic brute-force and side-channel validation attacks to enumerate active action binding IDs, parameter structures, and operational metadata, bypassing configured guest authentication barriers.