CVE-2023-28113 (Rust, published 2023-03-16T21:15Z)
Description: value: russh is a Rust SSH client and server library. Starting in version 0.34.0 and prior to versions 0.36.2 and 0.37.1, Diffie-Hellman key validation is insufficient, which can lead to insecure shared secrets and therefore breaks confidentiality. Connections between a russh client and server or those of a russh peer with some other misbehaving peer are most likely to be problematic. These may vulnerable to eavesdropping. Most other implementations reject such keys, so this is mainly an interoperability issue in such a case. This issue is fixed in versions 0.36.2 and 0.37.1
Severity: nan (CVSS v3 base score: 5.9)
Weakness: CWE-347 \u2014 Improper Verification of Cryptographic Signature
The product does not verify, or incorrectly verifies, the cryptographic signature for data.
Repository: https://github.com/warp-tech/russh
Files changed:
russh/src/kex/dh/groups.rs
russh/src/kex/dh/mod.rs
Commit message: GHSA-cqvm-j2r2-hwpg validate DH key range


Vulnerable code:
use std::ops::Shl;

    pub fn generate_private_key(&mut self) -> BigUint {
        self.private_key = rng.gen_biguint((self.exp_size * 8) - 2u64).shl(1);
        self.dh.generate_private_key();
        let server_pubkey = biguint_to_mpint(&self.dh.generate_public_key());
        exchange.server_ephemeral.extend(&server_pubkey);
        let shared = self
            .dh
            .compute_shared_secret(DH::decode_public_key(client_pubkey));
        self.dh.generate_private_key();
        let client_pubkey = biguint_to_mpint(&self.dh.generate_public_key());
        client_ephemeral.extend(&client_pubkey);
        buf.extend_ssh_string(&client_pubkey);

Fixed code:
    pub fn generate_private_key(&mut self, is_server: bool) -> BigUint {
        let q = (&self.prime_num - &BigUint::from(1u8)) / &BigUint::from(2u8);
        self.private_key = rng.gen_biguint_range(
            &if is_server {
                1u8.into()
            } else {
                2u8.into()
            },
            &q,
        );
    pub fn validate_shared_secret(&self, shared_secret: &BigUint) -> bool {
        let one = BigUint::from(1u8);
        let prime_minus_one = &self.prime_num - &one;

        shared_secret > &one && shared_secret < &prime_minus_one
    }


    pub fn validate_public_key(&self, public_key: &BigUint) -> bool {
        let one = BigUint::from(1u8);
        let prime_minus_one = &self.prime_num - &one;

        public_key > &one && public_key < &prime_minus_one
    }
use log::debug;
        self.dh.generate_private_key(true);
        let server_pubkey = &self.dh.generate_public_key();
        if !self.dh.validate_public_key(server_pubkey) {
            return Err(crate::Error::Inconsistent);
        }

        let encoded_server_pubkey = biguint_to_mpint(server_pubkey);
        exchange.server_ephemeral.extend(&encoded_server_pubkey);

        let decoded_client_pubkey = DH::decode_public_key(client_pubkey);
        if !self.dh.validate_public_key(&decoded_client_pubkey) {
            return Err(crate::Error::Inconsistent);
        }
        let shared = self.dh.compute_shared_secret(decoded_client_pubkey);
        if !self.dh.validate_shared_secret(&shared) {
            return Err(crate::Error::Inconsistent);
        }
        self.dh.generate_private_key(false);
        let client_pubkey = &self.dh.generate_public_key();

        if !self.dh.validate_public_key(client_pubkey) {
            return Err(crate::Error::Inconsistent);
        }
        let encoded_pubkey = biguint_to_mpint(client_pubkey);
        client_ephemeral.extend(&encoded_pubkey);
        buf.extend_ssh_string(&encoded_pubkey);

        if !self.dh.validate_public_key(&remote_pubkey) {
            return Err(crate::Error::Inconsistent);
        }

        if !self.dh.validate_shared_secret(&shared) {
            return Err(crate::Error::Inconsistent);
        }