Every CVE Search/ Get API key

CVE-2026-46673

Russh: Unchecked CryptoVec allocation and growth handling is reachable from local agent inputs in current russh releases and from remote SSH traffic in historical pre-0.58.0 releases

7.5 HighFix available

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
High

Exploit likelihood

0.26%Up 0.2 points since Jun 11, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.5

Russh: Unchecked CryptoVec allocation and growth handling is reachable from local agent inputs in current russh releases and from remote SSH traffic in historical pre-0.58.0 releases

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Record
CVE-2026-46673
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
EUVDEUVD-2026-36124 7.5

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Record
EUVD-2026-36124
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-g9f8-wqj9-fjw5 7.5

Russh: Unchecked CryptoVec allocation and growth handling is reachable

Title

Unchecked CryptoVec allocation and growth handling was reachable from local agent inputs in current russh releases and from remote SSH traffic in historical pre-0.58.0 releases

Summary

CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers.

Details

The underlying unsafe paths were in CryptoVec:

  • cryptovec/src/cryptovec.rs
    • unchecked capacity growth
    • unchecked length arithmetic in growth callers
    • raw allocation and reallocation paths coupled to those sizes
  • cryptovec/src/platform/unix.rs
    • mlock / munlock previously accepted zero-length calls and performed null-pointer validation inside the unsafe OS-call path

There are two relevant reachability stories:

  1. current local reachability in russh
  • russh/src/keys/agent/client.rs
    • AgentClient::read_response() read a peer-supplied u32 length and then resized self.buf to that value before reading the payload
  • russh/src/keys/agent/server.rs
    • Connection::run() read a peer-supplied u32 length and then resized self.buf to that value before reading the payload

This is the path that still existed in current 0.60.x releases before the fix, although by then those buffers were no longer CryptoVec.

  1. historical remote reachability in older russh
  • before commit 712e32b (first released in v0.58.0), non-secret transport and compression buffers in russh still used CryptoVec
  • I verified this in a detached pre-712e32b worktree by adding and running:
    • cipher::tests::remote_packet_length_grows_transport_cryptovec_buffer
    • compression::tests::remote_compressed_payload_expands_cryptovec_output
  • those tests show that remote SSH traffic could grow CryptoVec through:
    • transport packet reads
    • zlib decompression output

Also added a constrained-memory reproduction in that historical worktree:

  • compression::tests::remote_compressed_payload_can_crash_under_memory_limit

That test re-execs the test binary under prlimit --as=134217728, decompresses a highly compressible payload that expands to 96 MiB, and reliably aborts in the old Unix CryptoVec path when NonNull::new_unchecked() receives a null pointer after allocation failure.

The prepared patch does two things:

  1. hardens CryptoVec itself

    • checked capacity growth
    • checked length arithmetic
    • immediate allocation-failure handling
    • zero-length mlock / munlock no-ops
    • explicit null-pointer validation before entering the Unix unsafe locking calls
  2. hardens the real untrusted-input path

    • caps agent frame lengths at 256 * 1024 on both client and server before resizing buffers

This cap matches OpenSSH’s agent framing guardrail.

PoC

The following end-to-end tests demonstrate the real untrusted-input path by feeding oversized peer-controlled agent frame lengths into the public client and server flows and asserting that they are rejected before buffer growth.

Client-side agent reply path:

#[test]
fn oversized_agent_response_is_rejected_before_allocation() -> std::io::Result<()> {
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()?;

    runtime.block_on(async {
        let (mut writer, reader) = tokio::io::duplex(64);
        let server = tokio::spawn(async move {
            let mut frame = [0u8; 4];
            writer.read_exact(&mut frame).await?;
            let len = BigEndian::read_u32(&frame) as usize;
            let mut body = vec![0; len];
            writer.read_exact(&mut body).await?;

            BigEndian::write_u32(&mut frame, (MAX_AGENT_FRAME_LEN + 1) as u32);
           …
Record
GHSA-g9f8-wqj9-fjw5
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
NVD 7.5

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Record
CVE-2026-46673
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSV 7.5

Russh: Unchecked CryptoVec allocation and growth handling is reachable from local agent inputs in current russh releases and from remote SSH traffic in historical pre-0.58.0 releases

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Record
CVE-2026-46673
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSVDEBIAN-CVE-2026-46673 7.5

Russh is a Rust SSH client & server library. Prior to version 0.60.3, CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers. This issue has been patched in version 0.60.3.

Record
DEBIAN-CVE-2026-46673
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
OSVGHSA-g9f8-wqj9-fjw5 7.5

Russh: Unchecked CryptoVec allocation and growth handling is reachable

Title

Unchecked CryptoVec allocation and growth handling was reachable from local agent inputs in current russh releases and from remote SSH traffic in historical pre-0.58.0 releases

Summary

CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation/locking paths. In current russh releases, local SSH agent peers could still feed attacker-controlled frame lengths into buffer growth before validation. In older russh releases before 0.58.0, remote SSH traffic also reached CryptoVec through transport and compression buffers.

Details

The underlying unsafe paths were in CryptoVec:

  • cryptovec/src/cryptovec.rs
    • unchecked capacity growth
    • unchecked length arithmetic in growth callers
    • raw allocation and reallocation paths coupled to those sizes
  • cryptovec/src/platform/unix.rs
    • mlock / munlock previously accepted zero-length calls and performed null-pointer validation inside the unsafe OS-call path

There are two relevant reachability stories:

  1. current local reachability in russh
  • russh/src/keys/agent/client.rs
    • AgentClient::read_response() read a peer-supplied u32 length and then resized self.buf to that value before reading the payload
  • russh/src/keys/agent/server.rs
    • Connection::run() read a peer-supplied u32 length and then resized self.buf to that value before reading the payload

This is the path that still existed in current 0.60.x releases before the fix, although by then those buffers were no longer CryptoVec.

  1. historical remote reachability in older russh
  • before commit 712e32b (first released in v0.58.0), non-secret transport and compression buffers in russh still used CryptoVec
  • I verified this in a detached pre-712e32b worktree by adding and running:
    • cipher::tests::remote_packet_length_grows_transport_cryptovec_buffer
    • compression::tests::remote_compressed_payload_expands_cryptovec_output
  • those tests show that remote SSH traffic could grow CryptoVec through:
    • transport packet reads
    • zlib decompression output

Also added a constrained-memory reproduction in that historical worktree:

  • compression::tests::remote_compressed_payload_can_crash_under_memory_limit

That test re-execs the test binary under prlimit --as=134217728, decompresses a highly compressible payload that expands to 96 MiB, and reliably aborts in the old Unix CryptoVec path when NonNull::new_unchecked() receives a null pointer after allocation failure.

The prepared patch does two things:

  1. hardens CryptoVec itself

    • checked capacity growth
    • checked length arithmetic
    • immediate allocation-failure handling
    • zero-length mlock / munlock no-ops
    • explicit null-pointer validation before entering the Unix unsafe locking calls
  2. hardens the real untrusted-input path

    • caps agent frame lengths at 256 * 1024 on both client and server before resizing buffers

This cap matches OpenSSH’s agent framing guardrail.

PoC

The following end-to-end tests demonstrate the real untrusted-input path by feeding oversized peer-controlled agent frame lengths into the public client and server flows and asserting that they are rejected before buffer growth.

Client-side agent reply path:

#[test]
fn oversized_agent_response_is_rejected_before_allocation() -> std::io::Result<()> {
    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()?;

    runtime.block_on(async {
        let (mut writer, reader) = tokio::io::duplex(64);
        let server = tokio::spawn(async move {
            let mut frame = [0u8; 4];
            writer.read_exact(&mut frame).await?;
            let len = BigEndian::read_u32(&frame) as usize;
            let mut body = vec![0; len];
            writer.read_exact(&mut body).await?;

            BigEndian::write_u32(&mut frame, (MAX_AGENT_FRAME_LEN + 1) as u32);
           …
Record
GHSA-g9f8-wqj9-fjw5
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Weakness
CWE-770
Updated
2026-11-02
OSVRUSTSEC-2026-0153 7.5

Unchecked `CryptoVec` allocation and growth handling

CryptoVec used unchecked capacity growth, unchecked length arithmetic, and unsafe allocation and locking paths. In affected russh releases, attacker-controlled input could reach these code paths through buffer resizing operations.

Two affected reachability paths were identified:

  • Current russh releases (0.60.x before the fix) Local SSH agent peers could provide attacker-controlled frame lengths that were used to resize internal buffers before validation in:

    • AgentClient::read_response
    • agent::server::Connection::run
  • Historical russh releases before 0.58.0 CryptoVec was also used for non-secret transport and compression buffers, allowing remote SSH traffic to trigger CryptoVec growth through:

    • transport packet reads
    • zlib decompression output

These remote paths were removed in 0.58.0 when CryptoVec stopped being used for those buffers.

Under constrained memory conditions, historical russh versions prior to 0.58.0 can abort the process when remote compressed payload expansion causes allocation failure in CryptoVec. This was reproduced through the compression path and resulted in process termination in the Unix allocation/locking implementation after null pointer allocation failure.

For current affected releases, oversized local SSH agent frame lengths could trigger untrusted-input-driven buffer growth prior to validation.

No practical remote code execution, integrity or confidentiality impact has been demonstrated.

Fixed by validating CryptoVec growth operations and rejecting oversized SSH agent frame lengths before buffer allocation.

Record
RUSTSEC-2026-0153
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02
OSVRUSTSEC-2026-0154 7.5

Unbounded 32-bit allocation

Both the SSH agent server and client accepted peer-controlled frame lengths without enforcing a maximum frame size. This could cause large memory allocations while parsing a maliciously crafted agent frame.

A malicious peer could advertise an oversized frame length, causing the client or server to attempt a large memory allocation before validating the frame, potentially leading to memory exhaustion or process termination.

This is fixed by enforcing a maximum agent frame size of 256 KiB and rejecting oversized frames before buffer allocation.

Record
RUSTSEC-2026-0154
CVSS 3.1
7.5 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
russh-cryptovec
crates.io
through 0.60.2
0.60.3
russh
crates.io
through 0.60.2
0.60.3
rust-russh
Debian:14
before 0.64.1-1, plus listed versions
0.57.1-10.57.1-2
0.64.1-1
rust-russh-cryptovec
Debian:14
before 0.62.0-1, plus listed versions
0.52.0-10.58.0-10.59.0-1
0.62.0-1
russh-cryptovec
crates.io
before 0.60.3
0.60.3
russh
crates.io
before 0.60.3
0.60.3
russh-cryptovec
crates.io
0.0.0-0 up to 0.60.3
0.60.3
russh
crates.io
0.0.0-0 up to 0.60.3
0.60.3

History

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

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

In short

CVE-2026-46673 is a high vulnerability (CVSS 7.5) in Eugeny Russh, published May 15, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.26%, the 18th percentile. Fixed in russh-cryptovec 0.60.3.

References

Show 5 moreShow less
This page doesn't list everything

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

MoveEnter Open