A AegiFlow
MEDIUMCVSS 5.3

GHSA-g9hv-x236-4qp3

Russh: client wrong-length X25519 `clone_from_slice` panic (pre-auth DoS)

Published
2026-07-24
Modified
2026-07-24
Sources
github-advisory

Summary

### Summary A malicious SSH server can crash a `russh` client session with a single malformed key-exchange reply, causing a pre-authentication Denial-of-Service before the server host key is verified. The embedding process itself stays up, but the connection is killed deterministically. ### Details Every *other* kex path in `russh` validates the peer ephemeral length before cloning: - `Curve25519Kex::server_dh` (`russh/src/kex/curve25519.rs:61-65`) checks `if pubkey_len != 32 { return Err(crate::Error::Kex); }` before `clone_from_slice`. - The hybrid ML-KEM, ECDH-NIST, and DH/GEX paths all validate lengths. Only the client-side curve25519 `compute_shared_secret` is missing the check. This asymmetric validation gap makes the bug easy to miss in code review: a malicious *client* cannot panic a `russh` server this way (the server path checks the length), but a malicious *server* can panic a `russh` client. Incriminated source code (repo-relative paths): - Vulnerable `compute_shared_secret`: `russh/src/kex/curve25519.rs:110-117` (panic at line 113) - Client-side entry point: `russh/src/client/kex.rs:266-277` (`KEX_ECDH_REPLY` → `Bytes::decode` → `compute_shared_secret`) - Server-side contrast (has the length check): `russh/src/kex/curve25519.rs:51-88` (`server_dh`) - Session spawn site: `russh/src/client/mod.rs` (`connect_stream` → `russh_util::runtime::spawn`) - Runtime wrapper: `russh-util/src/runtime.rs:37-48` (`spawn` wraps `tokio::spawn`; panic surfaces as `JoinError`) ### PoC A standalone, self-contained Cargo PoC is provided in `vuln_poc/vuln_002_client_wronglen_x25519_panic/` in this repo. It installs a global panic hook that sets an `AtomicBool` if any panic fires, starts a malicious raw SSH server on `127.0.0.1:0` that completes the SSH id and `KEXINIT` exchange, reads the client `KEX_ECDH_INIT`, and sends `KEX_ECDH_REPLY` with a 16-byte server ephemeral (instead of 32) and a fake signature. It then calls `russh::client::connect` with `Preferred::kex` set to `curve25519-sha256` and a handler that accepts any server key (the check is never reached because the client panics first) and prints a clear verdict. Build & run: ```bash cd vuln_poc/vuln_002_client_wronglen_x25519_panic cargo run --release ``` Expected output (verdict line, from a successful reproduction): ``` [poc] panic captured: panicked at russh/src/kex/curve25519.rs:113:25: copy_from_slice: source slice length (16) does not match destination slice length (32) [!] Vulnerability reproduced: russh client panicked in Curve25519Kex::compute_shared_secret on a wrong-length (16-byte) server ephemeral before verifying the host key signature (pre-auth client DoS). ``` The malicious payload is the `f` field of `KEX_ECDH_REPLY`: ``` MSG_KEX_ECDH_REPLY (1 byte, value 0x1f) string K_S (server host key blob — any valid-looking bytes) string f (server ephemeral — 16 bytes of 0x00 instead of 32) string signature (fake; never verified by the client) ``` The length prefix of `f` is `4` (u32 BE) = 16, followed by 16 bytes. The `russh` client decodes this into `exchange.server_ephemeral` (a `Vec ` of length 16) and passes it to `compute_shared_secret`, which panics on `clone_from_slice`. ### Impact **What kind of vulnerability:** CWE-704 (incorrect type conversion / cast — `clone_from_slice` length mismatch) → deterministic panic → pre-authentication per-connection Denial-of-Service. The attacker does not need the server's private key; any network position that can deliver a malformed `KEX_ECDH_REPLY` (a rogue server, or a MitM before authentication) suffices. **Who is impacted:** any deployment that uses `russh::client::connect` (or `connect_stream`) to connect to an attacker-controlled or MitM-reachable SSH server, and that negotiates `curve25519-sha256` (the default and most-preferred kex algorithm in `russh`). A single malformed `KEX_ECDH_REPLY` kills the client session; the attack is deterministic and single-pack

Affected packages

EcosystemPackageAffected versionsFixed versions
rustrussh0.62.4

Remediation: Upgrade to 0.62.4 or later.

References

Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.