Apr 19, 2026·6 min read·4 visits
Zebra nodes prior to version 4.3.1 are vulnerable to a persistent DoS attack. Authenticated clients sending partial HTTP requests followed by a TCP RST can trigger an unhandled panic in the RPC middleware.
A Denial of Service (DoS) vulnerability exists in the Zebra Zcash node's JSON-RPC interface. An authenticated attacker can crash the node daemon by abruptly terminating an HTTP request during the payload transmission phase, exploiting unhandled I/O errors in the zebra-rpc crate.
Zebra is an independent Zcash node implementation written in Rust, designed to validate transactions and maintain the network consensus state. The zebra-rpc crate provides the JSON-RPC interface, allowing node operators and external applications to query node data and submit transactions. This interface utilizes an HTTP middleware stack to process incoming RPC requests, validate authentication, and parse request payloads.
Vulnerability GHSA-29X4-R6JV-FF4W affects the error handling mechanisms within this middleware stack. Specifically, the node fails to gracefully handle sudden connection terminations during the payload read phase. If an authenticated client initiates a request but abruptly closes the TCP connection before transmitting the complete payload, the middleware encounters an unhandled I/O exception.
This unhandled exception triggers a process panic, resulting in an immediate and unrecoverable termination of the Zebra node daemon. Consequently, the vulnerability permits an authenticated attacker to execute a persistent Denial of Service (DoS) attack by repeatedly crashing the node using minimal network bandwidth.
The root cause of this vulnerability lies in the implementation of the HTTP request body parser within the zebra-rpc crate. When reading the incoming byte stream, the code expects the stream to remain open until the number of bytes specified in the Content-Length header is fully received. The underlying TCP stack passes data to the Rust asynchronous runtime, which accumulates the bytes into a buffer.
If the remote client sends a TCP Reset (RST) packet, the socket enters an error state. The asynchronous read operation then yields an std::io::Error indicating a broken pipe or connection reset by peer. This is an expected failure mode in network programming, as clients routinely disconnect unexpectedly.
In versions prior to 4.3.1, the zebra-rpc code failed to propagate this expected failure state up the call stack correctly. The implementation either utilized an unhandled .unwrap() on the I/O Result or propagated the error to an outer scope that interpreted the I/O failure as a critical application fault. This failure to differentiate between expected network interruptions and fatal application errors directly caused the node panic.
The remediation of this issue required modifying the HTTP middleware to intercept and safely process std::io::Error variants originating from the body payload stream. The patch ensures that interrupted connections return a handled error state rather than invoking the panic handler. The vulnerable code pattern typically manifests in Rust asynchronous stream processing where the error channel is ignored.
The following block illustrates the conceptual difference between the vulnerable implementation and the patched logic within typical Rust HTTP middleware handling partial streams:
// Vulnerable Implementation Concept
let body_bytes = hyper::body::to_bytes(req.into_body())
.await
.expect("Failed to read request body"); // Triggers panic on TCP RST
let json_payload: RpcRequest = serde_json::from_slice(&body_bytes)?;The corrected implementation explicitly matches the Result returned by the stream reader. If an I/O error occurs, the function logs a warning and gracefully terminates the specific request handler without affecting the broader daemon runtime.
// Patched Implementation Concept
let body_bytes = match hyper::body::to_bytes(req.into_body()).await {
Ok(bytes) => bytes,
Err(e) => {
tracing::warn!("Client disconnected during payload transmission: {}", e);
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::empty())
.unwrap());
}
};
let json_payload: RpcRequest = serde_json::from_slice(&body_bytes)?;By treating network disconnection as a routine client-side error, the system maintains stability. The middleware correctly responds with an HTTP 400 Bad Request internally, or simply drops the task context, preventing the application-wide crash.
Exploitation of GHSA-29X4-R6JV-FF4W requires the attacker to hold valid authentication credentials for the JSON-RPC interface. By default, Zebra restricts the RPC service to the localhost interface and mandates cookie-based authentication. An attacker must either bypass network isolation or possess local access with the necessary cookie to initiate the attack.
The exploitation sequence begins with the attacker establishing a standard TCP connection to the Zebra RPC port. The attacker transmits a valid HTTP POST request header containing correct authentication tokens and an artificially large Content-Length value. This setup informs the server to allocate resources and wait for the subsequent payload bytes.
Immediately after sending the partial JSON body, the attacker forces a TCP connection closure using a TCP RST packet. Tools like curl (using specific timeout parameters) or custom Python scripts utilizing raw sockets can reliably reproduce this behavior. The server encounters the EOF or RST prior to satisfying the Content-Length requirement, triggering the panic.
The impact of this vulnerability is strictly limited to Denial of Service. Because the flaw triggers a memory-safe Rust panic, there is no risk of arbitrary code execution, memory corruption, or information disclosure. The process aborts cleanly, and the operating system reclaims allocated resources.
However, the availability impact on the affected node is complete. A crashed Zebra daemon immediately ceases to validate new blocks, relay transactions, or serve RPC queries to dependent applications. If the node is used as part of a critical infrastructure stack, such as an exchange backend or a block explorer, the surrounding services will experience an outage until the daemon is restarted.
The overall severity is constrained by the authentication requirement and the default local-only binding of the RPC interface. An external attacker scanning the public internet cannot exploit this vulnerability unless the node operator explicitly exposes the RPC port and utilizes weak or default credentials. This mitigates the risk of a widespread, automated attack against the Zcash network topology.
The primary remediation for GHSA-29X4-R6JV-FF4W is upgrading the Zebra node daemon to version 4.3.1 or later. The fixed release replaces the brittle error handling in the zebra-rpc crate with robust validation that safely handles interrupted data streams. The upgrade process does not require a resynchronization of the blockchain state.
Node operators must verify their current version using the zebrad --version command. For deployments utilizing containerization, administrators should pull the latest official Docker images tagged v4.3.1 and restart the container instances. Proper testing should be conducted in a staging environment prior to updating production nodes.
If an immediate upgrade is unfeasible, operators must enforce strict network access controls. Administrators should verify that the JSON-RPC interface binds exclusively to 127.0.0.1 and is protected by a host-based firewall. Restricting RPC access to trusted internal IP addresses and automated tooling prevents unauthorized clients from initiating the malicious HTTP requests required for exploitation.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
zebra-rpc Zcash Foundation | < 4.3.1 | 4.3.1 |
zebrad Zcash Foundation | < 4.3.1 | 4.3.1 |
| Attribute | Detail |
|---|---|
| Advisory ID | GHSA-29X4-R6JV-FF4W |
| CWE Class | CWE-248 (Uncaught Exception) |
| Attack Vector | Network |
| Authentication Required | Yes |
| Base CVSS Score | 6.5 |
| Exploit Status | None |
| Patched Version | 4.3.1 |
The application fails to catch an exception resulting from an unexpected I/O state during network operations, leading to an unrecoverable process panic.