Every CVE Search/ Get API key

CVE-2026-102821

Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey

6.5 MediumFix available

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.36%Up 0.1 points since Sep 30, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.5

Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
CVE-2026-102821
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-770
Updated
2026-11-02
EUVDEUVD-2026-89204 6.5

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
EUVD-2026-89204
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-35g8-35p8-c8fw 6.5

Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey

Summary

A russh server can be driven to unbounded heap growth (process OOM / kill) by a peer that speaks only standard SSH messages, in the default configuration.

The peer starts a key re-exchange (sends SSH_MSG_KEXINIT) but never sends the follow-up SSH_MSG_KEX_ECDH_INIT, leaving the server's kex state machine in SessionKexState::InProgress indefinitely. While a rekey is in progress the server's three message-drain paths are all gated off (if !self.kex.active()), but the network-read path stays active, so every SSH_MSG_CHANNEL_OPEN the peer sends is processed inline and appends one reply to an unbounded internal queue (priority_receiver, an UnboundedReceiver) that is not dequeued until the rekey completes. Because the peer decides whether the rekey ever completes, the queue — and the server's memory — grows without bound.

This is reproducible end-to-end against a real russh server over a real encrypted transport; see "Proof of concept". A one-line negative control (same flood, no rekey) keeps memory flat, isolating the rekey window as the sole trigger.

Impact

  • Availability / DoS. Server RSS climbs at roughly 3.3 KB per CHANNEL_OPEN, driven entirely by the peer, until the process is OOM-killed. In the reproduction one connection pushed the server from ~4 MB to 2.57 GB (and past 4.8 GB against the stock echoserver example) and it was still climbing when the flood was stopped.
  • Reached with standard messages, no special configuration. Any handler is affected, including one that rejects every channel (the reply enqueued on rejection is exactly what accumulates). There is no per-connection cap on in-flight channel opens or on the queue, and the queue's sender has no backpressure.
  • The peer keeps the connection alive simply by continuing to send, so the inactivity timer never fires.

Affected component

  • russh/src/server/session.rs — Session::run tokio::select! loop. The network-read arm is ungated; the three drain paths are gated on !self.kex.active().
  • russh/src/server/mod.rs — reply() routes a non-kex message received during a rekey straight to server_read_encrypted (inline processing).
  • russh/src/lib_inner.rs — ChannelOpenHandleInner's accept/reject/Drop all send a reply on an UnboundedSender.

Verified on v0.63.1 (commit d3ae702), which is the latest release. The gating logic predates it.

Details

Session::run (russh/src/server/session.rs:631) drives a tokio::select! (:713). Three of its message-drain sites are gated on !self.kex.active():

  • the pre-select! batch drain of priority_receiver/receiver (session.rs:680),
  • the priority_receiver.recv() arm (session.rs:762),
  • the receiver.recv() arm (session.rs:770, which also holds the only other priority_receiver drain at :777).

The fourth arm, r = &mut reading (session.rs:714), is ungated: it reads and processes one incoming packet every loop iteration regardless of rekey state, calling reply() (server/mod.rs:1128).

During a rekey, session.common.encrypted.is_some(), so the strict-kex message-ordering guard (server/mod.rs:1143, which is additionally gated on encrypted.is_none()) does not apply. A non-kex message therefore falls through reply() to session.server_read_encrypted(handler, pkt) (server/mod.rs:1232) and is handled inline. For SSH_MSG_CHANNEL_OPEN this reaches the channel-open handling, which hands the application a ChannelOpenHandle.

Whether the handler accepts or (the trait default) rejects, the handle's accept / reject / Drop all send a Msg::ChannelOpenReply on an UnboundedSender (russh/src/lib_inner.rs:560-603; Drop sends AdministrativelyProhibited at :594-603). That sender feeds priority_receiver, declared UnboundedReceiver<Msg> (session.rs:23) and created with tokio::sync::mpsc::unbounded_channel() (session.rs:1522).…

Record
GHSA-35g8-35p8-c8fw
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-770
Updated
2026-11-02
NVD 6.5

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
CVE-2026-102821
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-770
Updated
2026-11-02
OSV 6.5

Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
CVE-2026-102821
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-770
Updated
2026-11-02
OSVDEBIAN-CVE-2026-102821 6.5

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
DEBIAN-CVE-2026-102821
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
OSVGHSA-35g8-35p8-c8fw 6.5

Russh: Unbounded memory exhaustion via CHANNEL_OPEN flood during a client-stalled rekey

Summary

A russh server can be driven to unbounded heap growth (process OOM / kill) by a peer that speaks only standard SSH messages, in the default configuration.

The peer starts a key re-exchange (sends SSH_MSG_KEXINIT) but never sends the follow-up SSH_MSG_KEX_ECDH_INIT, leaving the server's kex state machine in SessionKexState::InProgress indefinitely. While a rekey is in progress the server's three message-drain paths are all gated off (if !self.kex.active()), but the network-read path stays active, so every SSH_MSG_CHANNEL_OPEN the peer sends is processed inline and appends one reply to an unbounded internal queue (priority_receiver, an UnboundedReceiver) that is not dequeued until the rekey completes. Because the peer decides whether the rekey ever completes, the queue — and the server's memory — grows without bound.

This is reproducible end-to-end against a real russh server over a real encrypted transport; see "Proof of concept". A one-line negative control (same flood, no rekey) keeps memory flat, isolating the rekey window as the sole trigger.

Impact

  • Availability / DoS. Server RSS climbs at roughly 3.3 KB per CHANNEL_OPEN, driven entirely by the peer, until the process is OOM-killed. In the reproduction one connection pushed the server from ~4 MB to 2.57 GB (and past 4.8 GB against the stock echoserver example) and it was still climbing when the flood was stopped.
  • Reached with standard messages, no special configuration. Any handler is affected, including one that rejects every channel (the reply enqueued on rejection is exactly what accumulates). There is no per-connection cap on in-flight channel opens or on the queue, and the queue's sender has no backpressure.
  • The peer keeps the connection alive simply by continuing to send, so the inactivity timer never fires.

Affected component

  • russh/src/server/session.rs — Session::run tokio::select! loop. The network-read arm is ungated; the three drain paths are gated on !self.kex.active().
  • russh/src/server/mod.rs — reply() routes a non-kex message received during a rekey straight to server_read_encrypted (inline processing).
  • russh/src/lib_inner.rs — ChannelOpenHandleInner's accept/reject/Drop all send a reply on an UnboundedSender.

Verified on v0.63.1 (commit d3ae702), which is the latest release. The gating logic predates it.

Details

Session::run (russh/src/server/session.rs:631) drives a tokio::select! (:713). Three of its message-drain sites are gated on !self.kex.active():

  • the pre-select! batch drain of priority_receiver/receiver (session.rs:680),
  • the priority_receiver.recv() arm (session.rs:762),
  • the receiver.recv() arm (session.rs:770, which also holds the only other priority_receiver drain at :777).

The fourth arm, r = &mut reading (session.rs:714), is ungated: it reads and processes one incoming packet every loop iteration regardless of rekey state, calling reply() (server/mod.rs:1128).

During a rekey, session.common.encrypted.is_some(), so the strict-kex message-ordering guard (server/mod.rs:1143, which is additionally gated on encrypted.is_none()) does not apply. A non-kex message therefore falls through reply() to session.server_read_encrypted(handler, pkt) (server/mod.rs:1232) and is handled inline. For SSH_MSG_CHANNEL_OPEN this reaches the channel-open handling, which hands the application a ChannelOpenHandle.

Whether the handler accepts or (the trait default) rejects, the handle's accept / reject / Drop all send a Msg::ChannelOpenReply on an UnboundedSender (russh/src/lib_inner.rs:560-603; Drop sends AdministrativelyProhibited at :594-603). That sender feeds priority_receiver, declared UnboundedReceiver<Msg> (session.rs:23) and created with tokio::sync::mpsc::unbounded_channel() (session.rs:1522).…

Record
GHSA-35g8-35p8-c8fw
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-400, CWE-770
Updated
2026-11-02
OSVRHSA-2026:74586 7.5

Red Hat Security Advisory: Red Hat Hardened Images RPMs Security Update

Record
RHSA-2026:74586
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
Updated
2026-11-02
OSVUBUNTU-CVE-2026-102821 6.5

Russh is a Rust SSH client and server library. Prior to 0.63.2, an authenticated remote peer can send SSH_MSG_KEXINIT without the required SSH_MSG_KEX_ECDH_INIT and then flood SSH_MSG_CHANNEL_OPEN messages while SessionKexState::InProgress prevents priority_receiver in russh/src/server/session.rs from being drained. The server continues processing network input and enqueues a ChannelOpenReply for each request on an unbounded channel, allowing one connection to grow memory until the process is terminated. This issue is fixed in version 0.63.2.

Record
UBUNTU-CVE-2026-102821
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
russh
crates.io
through 0.63.1
0.63.2
rust-russh
Debian:14
before 0.64.1-1, plus listed versions
0.57.1-10.57.1-2
0.64.1-1
russh
crates.io
before 0.63.2
0.63.2
openshell
Red Hat:hummingbird:1
From an advisory covering 4 CVEs
before 0:0.1.2-0.2.hum1
0:0.1.2-0.2.hum1
openshell-gateway
Red Hat:hummingbird:1
From an advisory covering 4 CVEs
before 0:0.1.2-0.2.hum1
0:0.1.2-0.2.hum1
python3-openshell
Red Hat:hummingbird:1
From an advisory covering 4 CVEs
before 0:0.1.2-0.2.hum1
0:0.1.2-0.2.hum1
rust-russh
Ubuntu:26.10
All versions, plus listed versions
0.57.1-2
None known

History

History
RecordedChangedSources
affected, facts
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-102821 is a medium vulnerability (CVSS 6.5) in Eugeny Russh, published September 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 29th percentile. Fixed in russh 0.63.2.

References

Show 31 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open