KEVWire

Security intelligence, with context.

Response snapshot Oct 31, 2026, 01:26 UTC

Back to the wire
Permalink
VulnerabilityCVE-2026-116773

russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets

All evidence, reproduction steps, and scripts in the report were verified against a real running instance. Nothing is fabricated or synthetic; every verdict is backed by captured output from an actual deployment. Concretely, the evidence comes from executing the real russh v0.64.

At a glance

Exploited
Not known
Severity
4.3 Medium (CVSS 3.1)
Chance of exploitation
Not estimated
Sources
1 source

What to do

GitHub lists first patched versions: russh: 0.65. Confirm the affected range and upgrade guidance in the exact advisory.

Read GitHub-reviewed advisory GHSA-4p9f-7p9m-5vfg (opens a new tab)

What happened

Summary

All evidence, reproduction steps, and scripts in the report were verified against a real running instance. Nothing is fabricated or synthetic; every verdict is backed by captured output from an actual deployment. Concretely, the evidence comes from executing the real russh v0.64.

1 client packet parser (Session::process_packet handling handle_ext_info) in a live client session with attacker-supplied packet bodies in the pre-authentication state; the captured output below is from that real execution.

russh's client stores every extension name it parses out of an EXT_INFO packet into a per-session vector and never clears, caps, or deduplicates it (russh/src/client/encrypted.rs:433, field declared at russh/src/session.rs:60).

Continue reading

The packet is accepted without limit in the pre-authentication states, so a server - which is exactly the attacker position for a client library, and remains one even after the client has verified its host key - can send an unbounded sequence of these packets and grow the client's memory for as long as the session lives: 400 packets of about 26 KB each, carrying 3000 extension names apiece, retained 49.

1 MiB (about 4.8 times the wire bytes) with the vector reaching 1,200,000 entries, while an equal volume of banner packets retained nothing.

Two details make this reachable in practice: russh accepts EXT_INFO in its pre-auth states with no budget of any kind (its automatic rekey read-limit only engages after authentication), and russh encodes EXT_INFO as its own private message number 7 rather than the 8308 value 82, so the check that might interoperate with a well-behaved peer is inoperative - the malicious server simply speaks number 7.

The same growth pattern has been accepted by the project before for other pre-auth allocation sinks; this one is a cross-packet accumulator rather than a single-packet allocation, and none of the published advisories covers it.

Details

The handler appends every parsed extension name to the session state with no bound:

russh/src/client/encrypted.rs:433 (v0.64.1 = main = e5d80a07dc554480b6ea3b2e14adfc190407b627)
 enc.received_extensions.push(name.clone()); // no cap, no dedup
russh/src/session.rs:60
 pub received_extensions: Vec , // lives for the whole session

The state machine routes EXT_INFO to this handler in both the WaitingAuthServiceRequest and WaitingAuthRequest states (encrypted.rs:111, encrypted.rs:401), i.e. before the user has authenticated, and the read-limit based automatic rekey that would otherwise throttle a flood is only allowed in the authenticated state, so no counter or budget applies pre-auth.

The message number involved is russh's private constant msg::EXT_INFO = 7 (russh/src/msg.

rs:32, in place since 2022), while RFC 8308 assigns EXT_INFO the value 82: russh does advertise the ext-info capability in its key exchange initiation, so a standards-compliant peer would send the packet as message number 82 - which this parser does not process - but nothing prevents a hostile or compromised server from sending message number 7 directly, and the client parses it unconditionally.

Each accepted entry costs several times its wire size once heap and string overhead are counted, and the vector is only ever read (an extension lookup by the awaiter layer), never trimmed. The affected code is identical on the latest tag v0.64.1 and on main (same commit e5d80a07dc554480b6ea3b2e14adfc190407b627), and the accumulation has existed since 2022, so all released client versions are affected.

Proof of concept

The harness constructs a real client Session in the WaitingAuthRequest pre-auth state (the configuration a client holds while the server is asking for authentication), then feeds attacker-controlled packet bodies to the genuine Session::process_packet dispatch - the same function the network event loop calls - so the retention happens in the production parsing path, not a reimplementation.

A counting global allocator measures live heap across packets. The control leg feeds the same packet volume of USERAUTH_BANNER bodies, which parse and dispatch without retention. The test below is added to the russh source tree as russh/src/client/extinfo_poc.rs, with two lines added to russh/src/client/mod.rs next to the existing test module declarations:

#[cfg(test)]
mod extinfo_poc;
cat > russh/src/client/extinfo_poc.rs 
//! handle_ext_info) with attacker-supplied packet bodies over an unmodified
//! client session in the pre-auth state (WaitingAuthRequest). Measuring
//! allocator totals proves the growth is persistent and unbounded across
//! packets. Control leg: equal traffic of IGNORE packets.
//!
//! Run: cargo +stable-x86_64-pc-windows-gnu test -p russh --lib extinfo_poc -- --nocapture

use std::alloc::{GlobalAlloc, Layout, System};
use std::collections::HashMap;
use std::num::Wrapping;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use tokio::sync::mpsc::{channel, unbounded_channel};

use super::*;
use crate::auth::{AuthRequest, Method};
use crate::compression::{Compression, Decompress};
use crate::kex::{KEXES, NONE};
use crate::msg;
use crate::session::{CommonSession, Encrypted, EncryptedState, Exchange};
use crate::sshbuffer::PacketWriter;
use crate::{cipher, CryptoVec, mac};

static LIVE: AtomicU64 = AtomicU64::new(0);

struct Counting;

unsafe impl GlobalAlloc for Counting {
 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
 let p = System.alloc(layout);
 if !p.is_null() {
 LIVE.fetch_add(layout.size() as u64, Ordering::SeqCst);
 }
 p
 }
 unsafe fn dealloc(&self, p: *mut u8, layout: Layout) {
 LIVE.fetch_sub(layout.size() as u64, Ordering::SeqCst);
 System.dealloc(p, layout)
 }
}

#[global_allocator]
static POC_ALLOC: Counting = Counting;

fn live_mb() -> f64 {
 LIVE.load(Ordering::SeqCst) as f64 / (1024.0 * 1024.0)
}

struct PocHandler;

impl Handler for PocHandler {
 type Error = crate::Error;

 async fn c

Affected package ranges reported by GitHub:
russh: < 0.65

Scores and how they were measured

CVSS base severity

4.3

Version 3.1As of

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L

Measurement source (opens a new tab)

How bad it is if exploited. Not a prediction of exploitation or proof you are exposed.

EPSS probability

Not available

A model's estimate of exploitation in the next 30 days, not certainty or your own risk.

Related technology

Sources

  1. Primary source

    GitHub-reviewed advisory GHSA-4p9f-7p9m-5vfg (opens a new tab)

    The public advisory supplies the description, affected package/version ranges, and any listed CVSS metric. GitHub review is not local review.

    Published Checked

  2. Primary source

    GitHub public advisory API page (opens a new tab)

    The API page supplied GHSA-4p9f-7p9m-5vfg, its actual identifiers, source dates, and structured metrics.

    Published Date not suppliedChecked

Cite this record

For factual claims, cite the primary sources above; cite KEVWire for its compilation, dates, and classifications.

APA

KEVWire. (2026, October 30). russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets (CVE-2026-116773). https://kevwire.com/record/CVE-2026-116773

MLA

"russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets (CVE-2026-116773)." KEVWire, 30 Oct. 2026, kevwire.com/record/CVE-2026-116773.

Chicago

KEVWire. "russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets (CVE-2026-116773)." Last modified October 30, 2026. https://kevwire.com/record/CVE-2026-116773.

Evidence summary (plain text)

russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets (Oct 30, 2026).
All evidence, reproduction steps, and scripts in the report were verified against a real running instance. Nothing is fabricated or synthetic; every verdict is backed by captured output from an actual deployment. Concretely, the evidence comes from executing the real russh v0.64.1 client packet parser (Session::process_packet handling handle_ext_info) in a live client session with attacker-supplied packet bodies in the pre-authentication state; the captured output below is from that real execution.
CVSS 4.3 (version 3.1; evidence as of Oct 30, 2026; https://github.com/advisories/GHSA-4p9f-7p9m-5vfg).
Sources:
GitHub-reviewed advisory GHSA-4p9f-7p9m-5vfg: https://github.com/advisories/GHSA-4p9f-7p9m-5vfg
GitHub public advisory API page: https://api.github.com/advisories?type=reviewed&per_page=100&sort=updated&direction=desc
Record ID: CVE-2026-116773.
Record origin: Ingested record.
AI evidence: Not assessed.
Known exploitation: Not assessed.
Zero-day status: Not assessed. KEV membership alone does not establish zero-day status.
About this data

This record was ingested from a data source. Retrieved: Oct 31, 2026, 00:50 UTC. Ingestion is not an independent editorial investigation.

A CISA KEV listing documents known exploitation, not necessarily a zero-day; absence from the catalog does not prove a vulnerability has never been exploited.

Technology tags indicate possible relevance, not verified exposure. Confirm the affected product, version, and configuration against your own inventory; KEVWire does not scan your systems.

A personal reading checklist on this device, not verified remediation.

Record JSON

Your account

Email alerts

Email alerts are coming as part of KEVWire Premium: join the waitlist. To follow your stack today, use its RSS feed, which honors your filters, or connect an AI agent through the API.

Suggest an improvement

Ideas, missing sources, corrections, and bugs are all welcome. No account needed. Suggestions are reviewed by a person before anything changes.

Record details

Record detail