Zebra getaddresstxids RPC response amplification
Zebra's getaddresstxids JSON-RPC method returns every transaction identifier for a queried transparent address without pagination, enabling a ~116-byte request to trigger a response that scales linearly with the number of transactions associated with the address, measured at approximately 578× amplification under controlled testing.
Why this matters
Operators who expose Zebra's RPC endpoint remotely may experience bandwidth exhaustion and elevated memory usage. The amplification is bounded per query only by the rpc.max_response_body_size configuration (default 50 MiB) and is unbounded across repeated queries, allowing sustained denial-of-service impact from a small request stream. Unlike the related getaddressutxos method, getaddressbalance returns a scalar value (approximately 1× amplification) and does not exhibit the same amplification behaviour, providing a control point for discriminating between amplifying and non-amplifying address queries.
How the attack works
The attack targets the getaddresstxids method in Zebra's RPC interface. The implementation (in zebra-rpc methods.rs, which routes to zebra_state::ReadRequest) builds and returns the complete list of transaction identifiers for the queried transparent address without applying a pagination limit. The attacker first seeds a transparent address they control with multiple transactions (cheap to generate under Regtest conditions), then queries that address via getaddresstxids. The response size scales with the transaction count: approximately 67 bytes per transaction identifier. The response is returned as a complete JSON array, with the per-query ceiling set by rpc.max_response_body_size (50 MiB by default), allowing for maximum amplification of approximately 430,000× for a ~116-byte request before hitting the response-size cap. Repeated queries across multiple seeded addresses can drive sustained egress bandwidth consumption.
Affected systems
- Implementation: Zebra (
ZcashFoundation/zebra) - Version tested: 4.2.0 Regtest node (zebra-rpc)
- Component: Transparent address-index RPC endpoint, specifically the
getaddresstxidsmethod - Scope: The behaviour is the documented unpaginated method semantics, not a version-specific regression
- Reachability: Any remote client reaching a non-loopback-bound Zebra RPC endpoint; no authentication required
Evidence
- Publicly documented: The method semantics are documented by the Zcash project as an unpaginated list RPC; no CVE has been assigned for this behaviour
- Independently reproduced by NullRabbit: Corpus reproducers ship in the
nr-bundles-publicdataset under the primitivezcash_zebra_getaddresstxids_response_amp - Measured under controlled conditions: On a self-owned Zebra 4.2.0 Regtest node with N=1000 coinbase transactions at a controlled transparent address, the measured amplification is approximately 578× (~116-byte request to ~67-kilobyte response). For the related
getaddressutxosmethod, the amplification under the same conditions was measured at approximately 2,033×, reflecting the larger per-item JSON footprint (~235 bytes per UTXO entry compared to ~67 bytes per transaction identifier) - Inferred but not reproduced: Multi-core aggregate saturation under sustained concurrent queries has not been separately load-tested
Operator actions
Documented mitigations include:
- Implement pagination or a hard cap on the result count returned by address-index RPCs
- Reduce the
rpc.max_response_body_sizeconfiguration limit - Apply per-source rate limiting at the RPC or gateway level
- Restrict the RPC endpoint to loopback access only or place it behind an authenticating gateway that applies response-size controls
The advisory notes that a loopback-bound RPC (the default) or one behind a response-size-capping gateway is not affected.
Canonical references
- NRDAX technique: NRDAX-T0329 (family
response_amp). - NullRabbit advisory: NR-2026-061 — Zebra transparent-address index RPC response amplification (published 2026-07-15).
- Evidence bundle (Hugging Face): NullRabbit/nr-bundles-public — primitive
zcash_zebra_getaddresstxids_response_amp. - Reproducer:
chains/zcash/lab/drivers/zcash_zebra_rpc_amp.py. - Upstream primary source: Zcash getaddressutxos / getaddresstxids RPC docs (documented unpaginated list-RPC semantics).
Related attacks
- Same NRDAX technique (NRDAX-T0329) applied to other chains and implementations, including Solana's
multi_get_accounts, Ethereum'seth_getlogs, and other Zebra methods (getaddressutxos,getblocktemplate,getrawmempoolverbose mode). The advisory for NR-2026-061 documentszcash_zebra_getaddressutxos_response_amp(MEDIUM severity) alongsidezcash_zebra_getaddresstxids_response_amp(LOW severity)
Subscription call to action
Track attacks affecting Zebra nodes.
Analysis plus reproducer. No weaponised proof-of-concept code.
Related Posts
Zebra getaddressutxos RPC response amplification
Zebra's `getaddressutxos` RPC returns all UTXOs for a transparent address without pagination, allowing an attacker to amplify a ~116 B request into a ~236 KB response (≈2,033×) by seeding an address with dust UTXOs.
Amplifying go-ethereum RPC responses with an eth_simulateV1 block-number gap
A single unauthenticated eth_simulateV1 call with a gap between two block numbers makes go-ethereum synthesise up to ~256 full block headers, expanding a ~285-byte request into a ~434 KB response.
How a wide-range eth_getLogs query turns a few hundred bytes into an unbounded response on exposed go-ethereum RPC nodes
eth_getLogs returns every log matching a filter across the requested block span in one JSON-RPC response, so a tiny wide-range or broad-topic request forces an exposed go-ethereum node to scan the range and serialise an unbounded matched-log set — a response-amplification DoS payable by an unauthenticated caller.
