Skip to main content

russh CVE-2026-111062

LOW
Improper Verification of Cryptographic Signature (CWE-347)
2026-10-19 https://github.com/Eugeny/russh GHSA-j7h2-66j4-ghjf
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Connect with authorized RSA key
  2. Exploit
    Announce rsa-sha2-512 in publickey request
  3. Execution
    Attach ssh-rsa SHA-1 signature
  4. Impact
    Server accepts, bypassing algorithm policy

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A04:2025 - Cryptographic Failures
ASVS 5.0
V11 - Cryptography Signature verification
Exploitation Conditions

Exploitation requires the attacker to already possess a valid RSA private key that the russh-based SSH server accepts for publickey authentication - it grants nothing to anyone without an authorized key. It only has security impact on servers that rely on the requested algorithm name: specifically servers configured to reject SHA-1 (ssh-rsa) signatures (as OpenSSH does by default since 8.8) or servers that log/audit which signature algorithm a client authenticated with. Limiting factors: russh clients are unaffected; authentication strength beyond the hash choice is unchanged because a valid signature by the private key is still mandatory; and servers that neither enforce an algorithm policy nor record the algorithm are effectively unaffected. Authentication requirements are not confirmed from CVSS (none provided), but the description establishes that a valid authorized RSA key is a prerequisite.

Impact

Key quantitative signals are absent: no CVSS score or vector, no EPSS probability, and no CISA KEV listing are provided in the input, so there is no evidence of active exploitation or high exploitation likelihood. A working proof-of-concept (a self-contained unit test) is published in the vendor advisory, but PoC availability is not the same as in-the-wild exploitation. Crucially, the described impact is narrow and does NOT constitute an authentication bypass: the attacker must already hold a valid RSA private key that the server would accept, and a cryptographically valid signature by that key is still required. The flaw (CWE-347, improper verification of a signature's algorithm) only lets a legitimate key holder downgrade the signature hash (e.g. present an ssh-rsa/SHA-1 signature for a request announcing rsa-sha2-512) or cause the server to mis-record the algorithm. Real-world priority is therefore low-to-moderate: it matters almost exclusively to russh-based SSH servers that enforce an algorithm policy (e.g. refusing SHA-1 like OpenSSH 8.8+) or that log/audit the authenticating signature algorithm for compliance. Because no CVSS vector was supplied, authentication requirements cannot be asserted as a CVSS-derived fact, though the description makes a valid authorized key a hard prerequisite. Absence of EPSS/KEV data should be read as 'not provided,' not as 'confirmed safe.'

Exploit ScenarioDerived

A user holding a valid RSA key that is authorized on a russh-based SSH server - one configured to reject SHA-1 (ssh-rsa) signatures or to audit the signature algorithm used - sends a publickey authentication request that announces rsa-sha2-512 while attaching an ssh-rsa (SHA-1) signature. russh verifies the signature using the algorithm in the signature blob rather than the announced one, so authentication succeeds and the SHA-1 ban / audit record is bypassed. A reproducible PoC unit test is published in the vendor's GHSA advisory.

Monitor Track and reassess. Why: LOW severity (CVSS 0.1-3.9).
Patch available

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Oct 19, 2026

Signature-algorithm confusion in the russh Rust SSH server library (every release from 0.34.0-beta.1 through 0.63.3, plus main) allows a client to announce one publickey algorithm (e.g. rsa-sha2-512) while presenting a signature made with a different one (e.g. ssh-rsa/SHA-1) and still be authenticated, because the requested algorithm name is never compared against the signature's own algorithm. The practical impact is limited: the attacker must already hold an RSA private key that the server accepts, so authentication strength is not weakened - only servers that rely on the announced algorithm to block SHA-1 signatures (as OpenSSH does by default since 8.8) or to record/audit which algorithm a user authenticated with are meaningfully affected, giving low integrity impact. No CISA KEV listing, no EPSS score and no CVSS vector were supplied, so there is no evidence of active exploitation; the upstream advisory does include a proof-of-concept unit test that reproduces the acceptance of a SHA-1 signature, and the vendor has released a fix in 0.64.0.

RemediationAI

Upgrade to russh 0.64.0, the vendor-released version that adds the missing comparison between the announced algorithm and sig.algorithm() (release tag https://github.com/Eugeny/russh/releases/tag/v0.64.0; advisory https://github.com/Eugeny/russh/security/advisories/GHSA-j7h2-66j4-ghjf). Note that the patch commit referenced in the advisory data (a0ded76bdc352f8fbeaaeb175fb2a0c586fbc8f2) shows unrelated global-request plumbing in its diff, so treat the tagged 0.64.0 release, not the diff alone, as the authoritative fix and verify the added check is present in your vendored copy. If upgrading is not immediately possible, the practical compensating control is to stop trusting the algorithm name: do not rely on russh's announced-algorithm value for policy enforcement or audit records, and instead treat any RSA authentication as potentially SHA-1 signed - the trade-off is that you lose the ability to block SHA-1 via this field at all, and log entries naming rsa-sha2-512 become unreliable. A stronger but more disruptive workaround is to reject RSA keys entirely at the Handler::auth_publickey stage and require Ed25519 or ECDSA client keys, which removes the multi-algorithm ambiguity but forces key rotation for every RSA user. Servers that neither enforce an algorithm policy nor log the algorithm need no urgent action beyond normal upgrade cadence, since a valid signature from an authorized private key is still mandatory in every case.

Affected Packages

cargo russh fix: 0.64.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in cargo

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

russh is a Rust implementation of the SSH protocol (forked from thrussh) that uses the ssh-key crate for key and signature primitives. Under RFC 8332 §3, the signature algorithm in a publickey authentication request must match the algorithm name carried in the request, which is how servers enforce a policy such as 'no SHA-1'. In russh's server_read_auth_request_pk (russh/src/server/encrypted.rs), the announced name (pubkey_algo) is used only to decode the key via PublicKeyOrCertificate::decode; the derived hash algorithm is then discarded. The signature blob is decoded separately with Signature::decode, and ssh-key selects the digest from the signature's own algorithm (sig.algorithm()) when Verifier::verify runs. Since pubkey_algo and sig.algorithm() are never compared, an RSA key can sign with SHA-1 while the request claims rsa-sha2-512, verification succeeds, and Handler::auth_publickey is invoked. The flaw is CWE-347 (improper verification of cryptographic signature) and is inherited from thrussh 0.33.5. The mechanism shifted over time - releases up to 0.46 overwrote the key's hash with the algorithm read from the signature blob (pubkey.set_algorithm(algo_)), while 0.47/0.48 onward (after the migration to ssh-key) decode and verify the signature with its embedded algorithm - but the outcome is identical across versions. Only RSA keys (which admit several signature algorithms) were examined; other key types were not tested, and russh clients are not affected.

Know when SSH gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

vuln.today AI 4.3 MEDIUM

Remote over SSH (AV:N) but requires a valid authorized RSA key (PR:L); impact is limited to bypassing signature-algorithm policy and audit integrity (I:L), with no confidentiality or availability effect.

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

DescriptionCVE.org

Details

In russh/src/server/encrypted.rs, server_read_auth_request_pk (0.63.3, around lines 880-990):

  • The requested algorithm name (pubkey_algo) is read and only used to decode the key: PublicKeyOrCertificate::decode(&pubkey_algo, &pubkey_key). The derived hash_alg is then dropped (PublicKeyOrCertificate::PublicKey { ref key, .. }).
  • The signature blob is decoded (Signature::decode) and verified with Verifier::verify(&pubkey, &buf, &sig). ssh-key picks the hash from the signature's own algorithm (sig.algorithm()).
  • pubkey_algo and sig.algorithm() are never compared.

So for an RSA key, the request can say rsa-sha2-512 while the signature is ssh-rsa. The signature is valid for SHA-1, verification succeeds, and Handler::auth_publickey is called.

Affected versions: every russh release, from the first (0.34.0-beta.1, 2022-03) through 0.63.3 and current main. The behaviour is inherited from thrussh, which russh was forked from.

The mechanism changed over time, but the outcome is the same: the hash is always taken from the signature blob, never checked against the request.

  • Up to 0.46: after reading the algorithm name inside the signature blob, russh overwrites the key's hash with it (pubkey.set_algorithm(algo_)), then verifies.
  • From 0.47/0.48 (move to ssh-key): the signature is decoded with its own algorithm (sig_workaround::Sig::decode, later Signature::decode) and verified with it.

Confirmed by running the PoC below on 0.63.3. Confirmed by reading the code of server/encrypted.rs in 0.34.0-beta.1, 0.34.0, 0.36.0, 0.36.1, 0.37.1, 0.40.2, 0.43.0, 0.45.0, 0.46.0, 0.48.2, 0.50.4, 0.54.6, 0.58.1, 0.63.1, 0.63.3 and main (2026-09-30), and in thrussh 0.33.5.

PoC

A unit test in the existing test module of russh/src/server/encrypted.rs. It uses the module's own helpers (test_auth_session, sign_with_hash_alg, PrivateKeyWithHashAlg) and needs no network or configuration.

  1. Check out russh 0.63.3, or main.
  2. Paste the code below inside mod tests { ... } of russh/src/server/encrypted.rs.
  3. Run cargo test --lib poc_sha1.
rust
// ---- PoC: signature algorithm not checked against the requested one ----

/// Records whether `auth_publickey` (called only after the signature
/// verified) was reached.
#[derive(Default)]
struct AcceptAnyKey {
    authenticated: bool,
}

impl Handler for AcceptAnyKey {
    type Error = Error;

    async fn auth_publickey_offered(
        &mut self,
        _user: &str,
        _public_key: &PublicKey,
    ) -> Result<Auth, Self::Error> {
        Ok(Auth::Accept)
    }

    async fn auth_publickey(
        &mut self,
        _user: &str,
        _public_key: &PublicKey,
    ) -> Result<Auth, Self::Error> {
        self.authenticated = true;
        Ok(Auth::Accept)
    }
}

/// A signed publickey request that names `announced` as its public key
/// algorithm, but is signed with `hash` (`None` = `ssh-rsa`, SHA-1).
fn signed_request_announcing(
    user: &str,
    private_key: Arc<PrivateKey>,
    announced: &str,
    hash: Option<crate::keys::HashAlg>,
) -> Vec<u8> {
    let mut packet = Vec::new();
    packet.push(msg::USERAUTH_REQUEST);
    user.encode(&mut packet).unwrap();
    "ssh-connection".encode(&mut packet).unwrap();
    "publickey".encode(&mut packet).unwrap();
    1u8.encode(&mut packet).unwrap();
    announced.encode(&mut packet).unwrap();
    private_key.public_key().to_bytes().unwrap().encode(&mut packet).unwrap();

    let mut signed = Vec::new();
    CryptoVec::new().as_ref().encode(&mut signed).unwrap();
    signed.extend_from_slice(&packet);
    let signature =
        sign_with_hash_alg(&PrivateKeyWithHashAlg::new(private_key, hash), &signed).unwrap();
    signature.encode(&mut packet).unwrap();
    packet
}

#[tokio::test]
async fn poc_sha1_signature_accepted_when_rsa_sha2_512_is_announced() {
    let rsa = ssh_key::private::RsaKeypair::random(&mut rand::rng(), 2048).unwrap();
    let private = Arc::new(PrivateKey::from(rsa));
    let mut session = test_auth_session();
    let mut handler = AcceptAnyKey::default();

    // The request says rsa-sha2-512; the signature blob is ssh-rsa (SHA-1).
    let packet = signed_request_announcing("alice", private, "rsa-sha2-512", None);
    session.process_packet(&mut handler, &packet).await.unwrap();

    // RFC 8332 §3: the signature algorithm must be the requested one, so
    // this request must be rejected. On russh 0.63.3 it is accepted.
    assert!(
        !handler.authenticated,
        "an ssh-rsa (SHA-1) signature was accepted for a request announcing rsa-sha2-512"
    );
}

Result on unmodified 0.63.3:

test server::encrypted::tests::poc_sha1_signature_accepted_when_rsa_sha2_512_is_announced ... FAILED
panicked at src/server/encrypted.rs: an ssh-rsa (SHA-1) signature was accepted for a request announcing rsa-sha2-512

With the fix below, the test passes and the whole library suite still passes: cargo test --lib gives 186 passed.

Suggested fix: reject a mismatch through the existing reject_auth_request(until, ...) path, so the constant rejection time is kept.

diff
--- a/russh/src/server/encrypted.rs
+++ b/russh/src/server/encrypted.rs
@@ -1023,6 +1023,21 @@
                     map_err!(ensure_end(&signature_reader))?;
                     map_err!(ensure_end(r))?;

+                    // RFC 8332 §3: the signature must use the algorithm the
+                    // request names.
+                    let offered_algorithm = match pk_or_cert {
+                        PublicKeyOrCertificate::PublicKey { .. } => ssh_key::Algorithm::new(&pubkey_algo),
+                        PublicKeyOrCertificate::Certificate(_) => {
+                            <ssh_key::Algorithm as crate::helpers::AlgorithmExt>::new_certificate_ext(&pubkey_algo)
+                        }
+                    };
+                    if offered_algorithm.ok().as_ref() != Some(&sig.algorithm()) {
+                        debug!("signature algorithm does not match the requested one");
+                        auth_user.clear();
+                        reject_auth_request(until, &mut self.write, auth_request).await?;
+                        return Ok(());
+                    }
+
                     let is_valid = if accepted_probe_matches && user == auth_user {
                         true
                     } else {

Impact

This is a missing check on the signature algorithm (improper verification of a cryptographic signature's algorithm, CWE-347) in the server's publickey authentication.

Who is impacted: applications that use russh as an SSH server and rely on the requested algorithm to restrict, record or audit user-key signature algorithms. For example:

  • a server that refuses SHA-1 (ssh-rsa) signatures, as OpenSSH does by default since 8.8;
  • a server that logs which algorithm a client authenticated with.

Such a server can be made to accept a SHA-1 signature, or to record rsa-sha2-512 for one, by any holder of a valid RSA key.

Not impacted:

  • russh clients;
  • authentication strength beyond the choice of hash, since a valid signature by the private key is still required;
  • Te RSA keys were examined, the case where one key admits several signature algorithms (ssh-rsa, rsa-sha2-256, rsa-sha2-512). We did not test other key types.

Threat Intelligence Overview

Prevalence
Low
Rust SSH library used by specific Rust applications, not broadly deployed
Threat Intel
1
1 source
Data Quality
2 sources
Confidence: Low
CVE.org GHSA GitHub

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity LOW
CISA KEV No
Public POC No
EPSS N/A
Patch available Yes
Active threat No
Deciding rule
LOW severity (CVSS 0.1-3.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

Attack Technique

Information disclosure occurs when an application unintentionally exposes sensitive data that aids attackers in reconnaissance or directly compromises security.

Affected Products (CPE)CVE.org

pkg:rust/russh

More in SSH

View all
CVE-2014-6271 CRITICAL POC
9.8 Sep 24

GNU Bash through 4.3 processes trailing strings after function definitions in the values of environment variables, which

CVE-2014-6278 HIGH POC
8.8 Sep 30

GNU Bash through 4.3 bash43-026 does not properly parse function definitions in the values of environment variables, whi

CVE-2014-7169 CRITICAL POC
9.8 Sep 25

GNU Bash through 4.3 bash43-025 processes trailing strings after certain malformed function definitions in the values of

CVE-2012-6066 CRITICAL POC
9.3 Dec 04

freeSSHd.exe in freeSSHd through 1.2.6 allows remote attackers to bypass authentication via a crafted session, as demons

CVE-2014-6277 CRITICAL POC
10.0 Sep 27

GNU Bash through 4.3 bash43-026 does not properly parse function definitions in the values of environment variables, whi

CVE-2023-38408 CRITICAL POC
9.8 Jul 20

The PKCS#11 feature in ssh-agent in OpenSSH before 9.3p2 has an insufficiently trustworthy search path, leading to remot

CVE-2023-25136 MEDIUM POC
6.5 Feb 03

OpenSSH server (sshd) 9.1 introduced a double-free vulnerability during options.kex_algorithms handling. Rated medium se

CVE-2016-6210 MEDIUM POC
5.9 Feb 13

sshd in OpenSSH before 7.3, when SHA256 or SHA512 are used for user password hashing, uses BLOWFISH hashing on a static

CVE-2015-5600 HIGH POC
8.1 Aug 03

The kbdint_next_device function in auth2-chall.c in sshd in OpenSSH through 6.9 does not properly restrict the processin

CVE-2016-6515 HIGH POC
7.5 Aug 07

The auth_password function in auth-passwd.c in sshd in OpenSSH before 7.3 does not limit password lengths for password a

CVE-2024-6387 HIGH POC
8.1 Jul 01

Remote code execution in OpenSSH's sshd server (regression of CVE-2006-5051) allows unauthenticated remote attackers to

CVE-2019-6111 MEDIUM POC
5.9 Jan 31

An issue was discovered in OpenSSH 7.9. Rated medium severity (CVSS 5.9), this vulnerability is remotely exploitable, no

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy