← Back to Research
Research · August 10, 2026

Amplifying go-ethereum RPC responses with an eth_simulateV1 block-number gap

Simon Morley·5 min read

A single unauthenticated eth_simulateV1 call whose blockStateCalls skip from one block number to a much higher one makes go-ethereum synthesise every intervening block — up to ~256 full header objects — and return them all, expanding a ~285-byte request into a ~434 KB response with no per-response size cap on the transport.

Why this matters

eth_simulateV1 is go-ethereum's newer transaction-simulation endpoint (its implementation of the eth_simulate method specified in ethereum/execution-apis), and it is part of the default eth namespace — so any node with the JSON-RPC surface reachable off loopback exposes it without authentication. The amplification here is not driven by an expensive computation or a large input; it comes from a documented autofill behaviour — the caller chooses a gap between two block numbers, and the server fabricates and serialises one block object per skipped height. A size- or rate-limit keyed on request bytes sees a tiny call; the cost lands on the response side and on the node's serialisation work. Public and semi-public RPC endpoints, and nodes behind a load balancer that does not cap response size or per-source request rate, are the exposed surface.

How the attack works

  • Entry point: the eth_simulateV1 JSON-RPC method (default eth namespace).
  • Preconditions: the RPC endpoint is reachable by the client; no authentication is required.
  • Processing path: eth_simulateV1 (internal/ethapi/simulate.go) processes the blockStateCalls list in order. Each entry may carry a blockOverrides.number. When one entry's number skips ahead of the previous block, the handler fills the gap by synthesising empty blocks for every intervening height, each serialised as a full header object in the result. The fill is bounded at approximately 256 blocks.
  • Boundary crossed: the number of fabricated block objects is chosen by the attacker — as the gap between two tiny call entries — and no per-response size cap is applied before the result is built and returned.
  • Resulting behaviour: a sub-kilobyte request returns hundreds of kilobytes of synthesised header objects; the expansion factor is set by the requested gap, up to the ~256-block ceiling.

The mechanism is documented eth_simulateV1 block-autofill semantics, not a memory-safety bug: the node is doing what the method specifies when asked to simulate across a block-number gap. The lever is that the gap — and therefore the synthesised-object count — is caller-controlled and only loosely bounded.

Affected systems

  • Severity: MEDIUM — availability / bandwidth-and-CPU only (no funds, no consensus break, no authentication bypass), per NR-2026-058.
  • Chain: Ethereum.
  • Implementation: go-ethereum, observed on 1.17.4, exposing eth_simulateV1 (its implementation of the ethereum/execution-apis eth_simulate method). The behaviour follows from the documented block-autofill semantics; it is not stated as a version-specific regression, so a single tested version should not be read as the only affected one, nor as a claim about other clients.
  • Component / method: JSON-RPC eth_simulateV1.
  • Not affected: a node that keeps --http / --ws on loopback or behind an authenticating gateway, or one that caps eth_simulateV1 response size / block-span per source. The advisory does not establish an affected version range beyond "go-ethereum exposing eth_simulateV1"; the behaviour is the documented autofill semantics, not a version-specific regression.

Evidence

  • Publicly documented: eth_simulateV1 and its blockStateCalls / blockOverrides block-autofill behaviour are specified by the eth_simulate method in ethereum/execution-apis; the ~256-block fill bound is part of the go-ethereum implementation in internal/ethapi/simulate.go.
  • Independently reproduced by NullRabbit: a reproducer (eth_simulateV1_gapblock_autofill_header_breadth_amp, family response_amp, source_class: original) issues the gap request against a self-owned node; the captured bundle ships in the NullRabbit/nr-bundles-public dataset.
  • Measured under controlled conditions: on self-owned go-ethereum 1.17.4 running --dev over loopback, two blockStateCalls at block 0x1 and 0x100 produced a ~285-byte request → ~434 KB response (≈1,523×) made of 256 synthesised block objects; a control call for a single block returned ~1.7 KB. The measurement is lab-fidelity; it demonstrates the request-to-response expansion, not a production-throughput or exploitability claim.
  • Inferred but not reproduced: the aggregate impact of many concurrent gap requests follows from the absence of a per-source response/rate cap; it is reasoned from the single-request measurement, not separately load-tested here.

Operator actions

From the advisory (config / RPC-edge level, not a code fix — the method behaves as specified):

  • Cap the block-number span / synthesised-block count for eth_simulateV1 at the RPC edge.
  • Rate-limit per source and cap response size for the eth namespace on public-facing endpoints.
  • Keep --http / --ws on loopback or behind an authenticating gateway; do not expose the eth namespace to untrusted clients.
  • Detection idea (unconfirmed): watch for eth_simulateV1 calls whose blockStateCalls carry large gaps between blockOverrides.number values, or whose response size is disproportionate to request size. Treat this as a monitoring heuristic, not a validated signature.

Canonical references

Related attacks

  • Same NRDAX technique (NRDAX-T0329, Unbounded RPC Response Amplification): other response-amplification primitives on go-ethereum and Zebra where a small request drives an unbounded response. This brief covers only the eth_simulateV1 gap-block-autofill instance; the shared root is a missing per-response size bound, not a shared code path.

No other technique links are asserted.

Track attacks affecting go-ethereum: subscribe to NullRabbit research updates.

Analysis plus reproducer. No weaponised proof-of-concept code.

ethereumgo-ethereumrpcresponse_ampinfrastructure-security
Publication policy:Why we publish these findings →

Related Posts