Jun 26, 2026·5 min read·7 visits
Go's SSH client/server knownhosts verification bypassed revocation checks for Certificate Authorities, trusting certificates signed by explicitly revoked CA keys.
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.
The golang.org/x/crypto/ssh/knownhosts package parses OpenSSH known_hosts files and provides callbacks to verify host key configurations. It is designed to evaluate standard host keys, trust-delegated Certificate Authorities, and explicitly revoked keys. When configured, it registers a key database capable of parsing the @revoked marker to terminate trust for compromised credentials.
In standard SSH certificate-based authentication architectures, a Certificate Authority is established to sign individual host certificates. If a CA is decommissioned or its private key is compromised, administrators mark the CA public key as @revoked in the client configuration to invalidate all downstream certificates. The vulnerability allows certificates signed by such revoked authorities to continue to pass validation successfully.
This behavior bypasses the designed revocation controls. An attacker in possession of a compromised CA private key can exploit this to generate valid certificates for arbitrary servers, undermining client-to-server trust. The issue represents a critical flaw categorized under CWE-295 (Improper Certificate Validation).
The vulnerability stems from an incorrect lookup key extraction inside the hostKeyDB.IsRevoked unexported method. The ssh.CertChecker engine relies on an IsRevoked callback of type func(cert *ssh.Certificate) bool during verification to verify whether presented credentials are listed in the key revocation list.
When parsing a known_hosts file, any entry marked with @revoked is parsed and stored inside the db.revoked map. The key for this map is the string representation of the marshaled public key of the revoked entity. The original logic in IsRevoked was implemented as follows:
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
_, ok := db.revoked[string(key.Marshal())]
return ok
}When a client connects to a server, the server presents a host certificate (*ssh.Certificate). The client passes this certificate directly to IsRevoked. The original code marshaled this individual certificate and checked the resulting string against the revocation map.
However, if the administrator revoked the Certificate Authority itself, the map contained the marshaled public key of the CA, not the host certificate. The host certificate's outer signature structure does not match the raw CA public key structure. Because the callback failed to extract and inspect the signing authority's public key (key.SignatureKey), the verification function consistently returned false for any leaf certificate issued by the revoked authority.
The vulnerability is remediated by checking both the presented leaf certificate and its signing authority against the revocation list. The patch updates the validation callback to safely extract the signing key.
Here is the vulnerable implementation:
// Vulnerable Code
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
_, ok := db.revoked[string(key.Marshal())]
return ok
}Here is the corrected implementation introduced in the patch:
// Patched Code
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
if _, ok := db.revoked[string(key.Marshal())]; ok {
return true
}
if _, ok := db.revoked[string(key.SignatureKey.Marshal())]; ok {
return true
}
return false
}By executing key.SignatureKey.Marshal(), the validation engine correctly extracts the public key of the signing CA. It then performs a lookup in the db.revoked map. If either the specific host certificate or the parent Certificate Authority is marked as revoked, the method returns true, triggering an immediate authentication failure.
This fix is robust and complete. It addresses the architectural oversight directly without introducing alternative bypasses, ensuring that revocation inherits down to leaf certificates as expected in hierarchical PKI systems.
To exploit this vulnerability, an attacker must have acquired the private key of a Certificate Authority that was previously trusted but has since been marked as @revoked in the target's known_hosts database. The attack does not require any specialized user interaction.
The exploitation flow operates as follows. First, the attacker establishes a rogue SSH server at an IP address or hostname the client attempts to reach. Second, the attacker generates an SSH host certificate for the target hostname, signing it with the revoked CA private key. When the vulnerable client initiates a connection, the rogue server presents this certificate.
The vulnerable validation engine evaluates the certificate. Because the host certificate itself is not explicitly listed in the revocation database, the callback reports that the certificate is valid, completely ignoring the revoked status of the CA that signed it. The connection proceeds, allowing the attacker to conduct an Adversary-in-the-Middle attack, potentially capturing authentication tokens, session data, or cleartext credentials.
This vulnerability carries a CVSS score of 9.1 (Critical). Successful exploitation results in a complete loss of connection confidentiality and integrity, as clients are misled into establishing secure tunnels to untrusted and potentially malicious destinations.
While the vulnerability requires the attacker to possess a compromised CA key, the consequences of such compromises are severe in automated environments. Automated backup routines, configuration management workflows, and container deployment orchestrators frequently use SSH to run administrative actions. If these clients are redirected to a rogue host, the impact propagates quickly through the network.
Because the vulnerability exists at the protocol validation layer, detection cannot easily rely on network-level signatures without decrypting the SSH handshake. Mitigating the vulnerability at the dependency level remains the only reliable safeguard to protect automated systems from trust-validation failures.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
golang.org/x/crypto Go | < v0.52.0 | v0.52.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-295 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 9.1 |
| EPSS Score | 0.00368 |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not in CISA KEV |
The application does not validate or incorrectly validates a certificate, which can allow an attacker to spoof a trusted entity by presenting a malicious or revoked certificate.
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 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.
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.