GHSA-g6xm-f9xp-qq35: Low severity rust/russh vulnerability
Details
Affected versions and vulnerable location
- Confirmed present on default branch main at HEAD 0089c89c94753bebbec12b956c07a1cd38740379. - Crate version at HEAD: 0.62.4. - Vulnerable locations on current default branch: - russh/src/server/mod.rs:91 (pub maxauthattempts: usize) - russh/src/server/mod.rs:121 (default maxauthattempts: 10) - russh/src/server/encrypted.rs:89 (USERAUTHREQUEST dispatch into auth handler path) - russh/src/server/encrypted.rs:98 (self.common.authattempts += 1) - russh/src/server/encrypted.rs:53 (only runtime read of authattempts, used for initial reject timing, not attempt limiting) - Default-branch history check did not show a newer merged commit adding enforcement against config.maxauthattempts.
Reachability trace verified
1. Entry point: exported server API server::runstream in russh/src/server/mod.rs:1049. 2. Session run loop in russh/src/server/session.rs processes incoming packets and calls reply(...) (server/session.rs:725). 3. reply forwards encrypted packets to session.serverreadencrypted(...) (server/mod.rs:1221). 4. serverreadencrypted routes USERAUTHREQUEST to enc.serverreadauthrequest(...) (server/encrypted.rs:89). 5. On each request, self.common.authattempts += 1 executes (server/encrypted.rs:98). 6. No comparison against self.common.config.maxauthattempts is present in this runtime flow.
PoC
Reproduction steps and observed output
I did not run a full server process in this environment because Rust tooling is unavailable. I verified the issue from source and command output on the audited tree.
1. Show where maxauthattempts appears:
bash rtk rg -n "maxauthattempts" .scratch/russh/russh/src/server/mod.rs .scratch/russh/russh/src/server/encrypted.rs .scratch/russh/russh/src/server/session.rs
Observed:
text .scratch/russh/russh/src/server/mod.rs:91: pub maxauthattempts: usize, .scratch/russh/russh/src/server/mod.rs:121: maxauthattempts: 10, .scratch/russh/russh/src/server/mod.rs:148: .field("maxauthattempts", &self.maxauthattempts)
2. Show runtime auth-attempt handling:
bash rtk rg -n "authattempts == 0|authattempts \\+= 1" .scratch/russh/russh/src/server/encrypted.rs
Observed:
text 53: let initialnonerejectionwaituntil = if self.common.authattempts == 0 { 98: self.common.authattempts += 1;
3. Show production entrypoint-to-auth path references:
bash rtk rg -n "pub async fn runstream|match reply\\(|serverreadencrypted\\(|serverreadauthrequest\\(" .scratch/russh/russh/src/server/mod.rs .scratch/russh/russh/src/server/session.rs .scratch/russh/russh/src/server/encrypted.rs
Observed:
text .scratch/russh/russh/src/server/encrypted.rs:89: enc.serverreadauthrequest( .scratch/russh/russh/src/server/session.rs:725: match reply(&mut self, &mut handler, &mut pkt).await { .scratch/russh/russh/src/server/mod.rs:1049:pub async fn runstream<H, R>( .scratch/russh/russh/src/server/mod.rs:1221: session.serverreadencrypted(handler, pkt).await
4. Toolchain check:
bash cargo --version
Observed:
text /bin/bash: line 1: cargo: command not found
Impact
Attacker model
- Attacker: unauthenticated remote client with TCP reachability to a russh-backed SSH service. - Preconditions: deployer expects server::Config.maxauthattempts to cap attempts. - Impact: repeated USERAUTHREQUEST attempts continue for a single connection beyond configured limit, increasing online guessing opportunity and backend auth workload.
Suggested fix
Enforce maxauthattempts in the USERAUTHREQUEST branch before invoking auth-method handlers, and fail closed once threshold is reached.
Concrete patch direction in russh/src/server/encrypted.rs:
rust if self.common.config.maxauthattempts > 0 && self.common.authattempts >= self.common.config.maxauthattempts { self.common.disconnect( Disconnect::NoMoreAuthMethodsAvailable, "Too many authentication attempts", "", )?; return Ok(()); }
How it was found and a note on tooling
The researcher synthesized three lens outputs, then revalidated each claim against current main: source presence and commit history, advisory overlap checks in both GitHub advisories and OSV, entrypoint-to-sink reachability, attacker-model realism, and execution-claim integrity. The researcher used gh, git, rg, and direct source inspection under .scratch/russh. Because Rust tooling is unavailable in this worker, this report is intentionally marked source-only.
AI assistance was used while investigating this and while drafting this report. The finding was verified by reading the cited code at HEAD. The vulnerability was not executed it, and that limit is stated plainly above rather than left implied.
Credits: arpitjain099.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
rust/russhto a version that resolves this vulnerability.Fixed in 0.62.6 - Compensating control
In the USERAUTH_REQUEST branch of russh/src/server/encrypted.rs, enforce self.common.config.max_auth_attempts before invoking authentication-method handlers; fail closed when the threshold is reached by disconnecting with Disconnect::NoMoreAuthMethodsAvailable.
Event History
Frequently Asked Questions
Are default deployments affected?
Yes. On the confirmed default-branch HEAD, max_auth_attempts defaults to 10, but the provided trace shows no enforcement against config.max_auth_attempts.
Which deployments are exposed to this behavior?
Servers using the exported server::run_stream API and accepting SSH traffic can reach the USERAUTH_REQUEST handling path. The issue is reachable through incoming encrypted authentication packets.
What does an attacker need to exploit it?
The supplied severity vector indicates network access is required, with no privileges or user interaction required. Exploitation is rated high complexity, and the stated impact is limited to confidentiality.
How can I check whether my code has the issue?
Inspect the server authentication path for USERAUTH_REQUEST handling: auth_attempts is incremented, but there must also be a comparison that enforces config.max_auth_attempts. In the confirmed vulnerable default-branch state, the only runtime read of auth_attempts was for initial rejection timing rather than attempt limiting.