CVE-2026-73489
Russh: Post-auth remote panic via pty-req with more than 130 terminal-mode records
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
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
- Low
Exploit likelihood
What each source reports
CVE List Score used
4.3
Russh: Post-auth remote panic via pty-req with more than 130 terminal-mode records
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
- Record
CVE-2026-73489- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-129
- Updated
- 2026-11-02
EUVDEUVD-2026-58524
4.3
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
- Record
EUVD-2026-58524- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-cqjc-rmpq-xprq
4.3
Russh: Post-auth remote panic via pty-req with more than 130 terminal-mode records
Summary
A post-authentication denial-of-service panic in russh 0.62.2 (commit
c4be19f1915c8682f4615c3fd50008512b474491, current default branch main as
of 2026-07-22). An authenticated client sends a pty-req channel request
carrying more than 130 terminal-mode records. The parser uses a fixed
[(Pty::TTY_OP_END, 0); 130] array but increments its counter i for every
valid record (logging "too many pty codes" without returning), then slices
&modes[0..i] — an out-of-bounds slice that panics (range end index 131 out of range for slice of length 130) before the application pty_request
handler runs.
This is reachable with the default server configuration and the default
crypto config (curve25519-sha256 + chacha20-poly1305), requiring only an
authenticated session channel — no caller-supplied parameter. It is reproduced
end-to-end against the unmodified real russh 0.62.2 library (a real
russh::client + russh::server over TCP, using the public
Channel::request_pty(...) API); the PoC below links the real crate, not a
copied snippet. The defect is still present on main HEAD (v0.62.3,
2026-07-22) and is not covered by any of the 11 published russh GHSA
advisories (GHSA-4r3c-5hpg-58qr / CVE-2026-48110 is allocation-first string
parsing, not the fixed-array slice overflow; it was fixed in 0.61.0 but this
code path still overflows the fixed array).
Rust bounds-checked panics abort the task safely (no memory corruption / RCE); the impact is remote denial of service.
Details
russh/src/server/encrypted.rs, pty-req handling (lines 1137–1201):
let mut modes = [(Pty::TTY_OP_END, 0); 130]; // fixed 130-entry array (line 1137)
let mut i = 0;
...
while !mode_bytes.is_empty() {
let code = mode_bytes[0];
if code == 0 { if mode_bytes.len() != 1 { return Err(...); } break; }
if mode_bytes.len() < 5 { return Err(...); }
let num = BigEndian::read_u32(&mode_bytes[1..5]);
if let Some(code) = Pty::from_u8(code) {
if i < 130 {
modes[i] = (code, num);
} else {
error!("pty-req: too many pty codes"); // logs, does NOT return (line 1162)
}
}
i += 1; // keeps growing past 130
mode_bytes = &mode_bytes[5..];
}
...
handler.pty_request(channel_num, &term, ..., &modes[0..i], self).await // line 1201 — OOB when i > 130
Each terminal-mode record is exactly 5 bytes (1-byte opcode + 4-byte value),
and i += 1 runs for every valid record (including repeats of the same
opcode). The if i < 130 write-gate prevents an in-array overflow but the
counter still grows unbounded, and the later &modes[0..i] slice has no
corresponding bound. SSH packet-size limits do not bound the mode count to
130, so a single normal-sized pty-req can carry hundreds of mode records.
The client's own request_pty serialization (russh/src/client/session.rs:
((1 + 5 * terminal_modes.len()) as u32).encode(&mut enc.write)?; // line 129
for &(code, value) in terminal_modes {
if code == Pty::TTY_OP_END { continue; }
(code as u8).encode(&mut enc.write)?;
value.encode(&mut enc.write)?; // line 135
}
writes every record with no count cap, so 131 records reach the server as a legitimate authenticated channel request.
PoC
The PoC is a standalone examples/ binary that links the unmodified real
russh 0.62.2 crate and reproduces over a real TCP connection with the default
crypto config. It runs an ATTACK case (131 mode records → panic) and a CONTROL
case (130 mode records → parses fine), proving the panic is caused
specifically by exceeding the 130-entry array.
One-line reproducer
# Drop the .rs below into russh/examples/ of a checkout of
# Eugeny/russh @ c4be19f1915c (tag v0.62.2), then:
cargo +stable build --release --example e2e_c15_pty_modes_panic
RUST_BACKTRACE=1…
- Record
GHSA-cqjc-rmpq-xprq- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-129
- Updated
- 2026-11-02
NVD
4.3
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
- Record
CVE-2026-73489- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-129
- Updated
- 2026-11-02
OSV
4.3
Russh: Post-auth remote panic via pty-req with more than 130 terminal-mode records
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
- Record
CVE-2026-73489- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-129
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-73489
4.3
Russh is a Rust SSH client & server library. Prior to 0.62.4, an authenticated SSH client can cause a denial of service by sending a pty-req channel request with more than 130 terminal-mode records. The parser in russh/src/server/encrypted.rs stores terminal modes in a fixed 130-entry [(Pty::TTY_OP_END, 0); 130] array but continues increasing the mode count, then constructs an out-of-bounds slice and panics before the application pty_request handler runs. The panic terminates the server session task without causing memory corruption. This issue is fixed in version 0.62.4.
- Record
DEBIAN-CVE-2026-73489- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Updated
- 2026-11-02
OSVGHSA-cqjc-rmpq-xprq
4.3
Russh: Post-auth remote panic via pty-req with more than 130 terminal-mode records
Summary
A post-authentication denial-of-service panic in russh 0.62.2 (commit
c4be19f1915c8682f4615c3fd50008512b474491, current default branch main as
of 2026-07-22). An authenticated client sends a pty-req channel request
carrying more than 130 terminal-mode records. The parser uses a fixed
[(Pty::TTY_OP_END, 0); 130] array but increments its counter i for every
valid record (logging "too many pty codes" without returning), then slices
&modes[0..i] — an out-of-bounds slice that panics (range end index 131 out of range for slice of length 130) before the application pty_request
handler runs.
This is reachable with the default server configuration and the default
crypto config (curve25519-sha256 + chacha20-poly1305), requiring only an
authenticated session channel — no caller-supplied parameter. It is reproduced
end-to-end against the unmodified real russh 0.62.2 library (a real
russh::client + russh::server over TCP, using the public
Channel::request_pty(...) API); the PoC below links the real crate, not a
copied snippet. The defect is still present on main HEAD (v0.62.3,
2026-07-22) and is not covered by any of the 11 published russh GHSA
advisories (GHSA-4r3c-5hpg-58qr / CVE-2026-48110 is allocation-first string
parsing, not the fixed-array slice overflow; it was fixed in 0.61.0 but this
code path still overflows the fixed array).
Rust bounds-checked panics abort the task safely (no memory corruption / RCE); the impact is remote denial of service.
Details
russh/src/server/encrypted.rs, pty-req handling (lines 1137–1201):
let mut modes = [(Pty::TTY_OP_END, 0); 130]; // fixed 130-entry array (line 1137)
let mut i = 0;
...
while !mode_bytes.is_empty() {
let code = mode_bytes[0];
if code == 0 { if mode_bytes.len() != 1 { return Err(...); } break; }
if mode_bytes.len() < 5 { return Err(...); }
let num = BigEndian::read_u32(&mode_bytes[1..5]);
if let Some(code) = Pty::from_u8(code) {
if i < 130 {
modes[i] = (code, num);
} else {
error!("pty-req: too many pty codes"); // logs, does NOT return (line 1162)
}
}
i += 1; // keeps growing past 130
mode_bytes = &mode_bytes[5..];
}
...
handler.pty_request(channel_num, &term, ..., &modes[0..i], self).await // line 1201 — OOB when i > 130
Each terminal-mode record is exactly 5 bytes (1-byte opcode + 4-byte value),
and i += 1 runs for every valid record (including repeats of the same
opcode). The if i < 130 write-gate prevents an in-array overflow but the
counter still grows unbounded, and the later &modes[0..i] slice has no
corresponding bound. SSH packet-size limits do not bound the mode count to
130, so a single normal-sized pty-req can carry hundreds of mode records.
The client's own request_pty serialization (russh/src/client/session.rs:
((1 + 5 * terminal_modes.len()) as u32).encode(&mut enc.write)?; // line 129
for &(code, value) in terminal_modes {
if code == Pty::TTY_OP_END { continue; }
(code as u8).encode(&mut enc.write)?;
value.encode(&mut enc.write)?; // line 135
}
writes every record with no count cap, so 131 records reach the server as a legitimate authenticated channel request.
PoC
The PoC is a standalone examples/ binary that links the unmodified real
russh 0.62.2 crate and reproduces over a real TCP connection with the default
crypto config. It runs an ATTACK case (131 mode records → panic) and a CONTROL
case (130 mode records → parses fine), proving the panic is caused
specifically by exceeding the 130-entry array.
One-line reproducer
# Drop the .rs below into russh/examples/ of a checkout of
# Eugeny/russh @ c4be19f1915c (tag v0.62.2), then:
cargo +stable build --release --example e2e_c15_pty_modes_panic
RUST_BACKTRACE=1…
- Record
GHSA-cqjc-rmpq-xprq- CVSS 3.1
- 4.3 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L - Weakness
- CWE-129
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
russhcrates.io |
through 0.62.3 | 0.62.4 |
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.62.4 | 0.62.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| 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-73489 is a medium vulnerability (CVSS 4.3) in Eugeny Russh, published July 24, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.45%, the 39th percentile. Fixed in russh 0.62.4.
References
github.com/Eugeny/russh/security/advisories/GHSA-cqjc-rmpq-xprq
github.com/Eugeny/russh/commit/8912512371820167a12a0a638bd666856ce458ad
github.com/Eugeny/russh/releases/tag/v0.62.4
github.com/advisories/GHSA-cqjc-rmpq-xprq
github.com/CVEProject/cvelistV5/tree/main/cves/2026/73xxx/CVE-2026-73489.json
nvd.nist.gov/vuln/detail/CVE-2026-73489
security-tracker.debian.org/tracker/CVE-2026-73489
github.com/Eugeny/russh
Left out here: the rest of 2 source descriptions. The JSON record has them.