CVE-2026-102821
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.
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
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 Medium
CVSS: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 Medium
CVSS: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 stockechoserverexample) 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::runtokio::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 toserver_read_encrypted(inline processing).russh/src/lib_inner.rs—ChannelOpenHandleInner'saccept/reject/Dropallsenda reply on anUnboundedSender.
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 ofpriority_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 otherpriority_receiverdrain 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 Medium
CVSS: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 Medium
CVSS: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 Medium
CVSS: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 Medium
CVSS: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 stockechoserverexample) 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::runtokio::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 toserver_read_encrypted(inline processing).russh/src/lib_inner.rs—ChannelOpenHandleInner'saccept/reject/Dropallsenda reply on anUnboundedSender.
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 ofpriority_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 otherpriority_receiverdrain 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 Medium
CVSS: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 High
CVSS: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 Medium
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
russhcrates.io |
through 0.63.1 | 0.63.2 |
rust-russhDebian:14 |
before 0.64.1-1, plus listed versions 0.57.1-10.57.1-2 |
0.64.1-1 |
russhcrates.io |
before 0.63.2 | 0.63.2 |
openshellRed 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-gatewayRed 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-openshellRed 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-russhUbuntu:26.10 |
All versions, plus listed versions 0.57.1-2 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE 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
github.com/Eugeny/russh/security/advisories/GHSA-35g8-35p8-c8fw
github.com/Eugeny/russh/commit/a282af361ac99bc76b80876d1aae128e89dbf66b
github.com/Eugeny/russh/releases/tag/v0.63.2
nvd.nist.gov/vuln/detail/CVE-2026-102821
github.com/advisories/GHSA-35g8-35p8-c8fw
github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102821.json
Show 31 moreShow less
security-tracker.debian.org/tracker/CVE-2026-102821
github.com/Eugeny/russh
access.redhat.com/errata/RHSA-2026:74586
access.redhat.com/security/cve/CVE-2026-102821
access.redhat.com/security/cve/CVE-2026-102823
access.redhat.com/security/cve/CVE-2026-102824
access.redhat.com/security/updates/classification
images.redhat.com
access.redhat.com/security/cve/CVE-2026-102825
security.access.redhat.com/data/csaf/v2/advisories/2026/rhsa-2026_74586.json
bugzilla.redhat.com/show_bug.cgi?id=2543501
cve.org/CVERecord?id=CVE-2026-102821
bugzilla.redhat.com/show_bug.cgi?id=2543503
cve.org/CVERecord?id=CVE-2026-102823
nvd.nist.gov/vuln/detail/CVE-2026-102823
github.com/Eugeny/russh/commit/3430fd26ecafc0dc3705210f5f39a9119fa22774
github.com/Eugeny/russh/releases/tag/v0.63.1
github.com/Eugeny/russh/security/advisories/GHSA-47hw-gvq5-r2gm
bugzilla.redhat.com/show_bug.cgi?id=2543498
cve.org/CVERecord?id=CVE-2026-102824
nvd.nist.gov/vuln/detail/CVE-2026-102824
github.com/Eugeny/russh/commit/8da8967f196472576b1565d518a0ed60fce60f0c
github.com/Eugeny/russh/releases/tag/v0.63.0
github.com/Eugeny/russh/security/advisories/GHSA-w3jg-pjxf-73p4
bugzilla.redhat.com/show_bug.cgi?id=2543507
cve.org/CVERecord?id=CVE-2026-102825
nvd.nist.gov/vuln/detail/CVE-2026-102825
github.com/Eugeny/russh/commit/f8fd0b11a393364dc7f01a182482d86adafdd653
github.com/Eugeny/russh/releases/tag/v0.62.6
github.com/Eugeny/russh/security/advisories/GHSA-g6xm-f9xp-qq35
ubuntu.com/security/CVE-2026-102821
Left out here: the rest of 2 source descriptions. The JSON record has them.