Jun 26, 2026·5 min read·42 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 symbolic link directory traversal vulnerability was identified in go-git, a pure Go implementation of the Git specification. This vulnerability allows an attacker to construct a repository that, when checked out or processed, bypasses directory boundaries to write or overwrite arbitrary files on the host filesystem.
CVE-2026-71557 is a path traversal vulnerability in go-git, a pure-Go implementation of Git. In vulnerable versions, the filesystem-backed storage engine fails to validate reference names before mapping them to on-disk paths. An attacker hosting a malicious Git server can advertise references containing directory traversal sequences, such as 'refs/heads/../../config', to write or overwrite files outside the intended reference storage directory.
An information disclosure vulnerability in the Nuxt development server allows adjacent network attackers to retrieve the absolute project root directory and a persistent workspace UUID by querying the unprotected Chrome DevTools workspace endpoint. This occurs when the development server is bound to a network-reachable interface, allowing requests that bypass the header-based security verification checks.
A Regular Expression Denial of Service (ReDoS) vulnerability exists in SvelteKit's content negotiation header parser prior to version 2.70.2. An unauthenticated remote attacker can exploit this vulnerability by sending a crafted Accept header with highly repetitive malformed values. This triggers catastrophic backtracking on the single-threaded Node.js/Bun event loop, leading to CPU exhaustion and full denial of service.
An OS command injection vulnerability exists in the npm package loading component of the jsii-diff CLI tool within the AWS jsii framework. Prior to version 1.131.0, when parsing package specifiers prefixed with `npm:`, the tool concatenated user-controlled inputs directly into a shell execution string via child_process.exec. This allows attackers to execute arbitrary shell commands under the context of the running Node.js process.
CodeIgniter4 versions prior to v4.7.4 contain a protocol-spoofing vulnerability due to improper verification of upstream reverse proxy forwarding headers. Remote, unauthenticated attackers can inject headers like X-Forwarded-Proto to deceive the framework into identifying an insecure HTTP request as a secure HTTPS connection.