← Back to Research
Research · August 10, 2026

Burning go-ethereum RPC CPU with one eth_call to the BLAKE2F precompile

Simon Morley·5 min read

A single unauthenticated eth_call that invokes go-ethereum's BLAKE2F precompile with a large caller-set rounds value pins an RPC worker's CPU for hundreds of milliseconds from a sub-kilobyte request, and the JSON-RPC transport applies no per-source rate or concurrency cap — so concurrent calls each occupy a core.

Why this matters

Anyone running a go-ethereum node with the eth JSON-RPC namespace reachable off loopback exposes eth_call, which executes an attacker-influenced message synchronously on an RPC worker. The BLAKE2F precompile turns that into a precise CPU dial: the number of compression rounds is a field the caller sets, and the work scales linearly with it. Because the request stays small and the response is a normal (often error) result, there is no byte amplification to trip a size-based filter, and a naïve request-rate limit does not help — the cost is per request, and a handful of concurrent requests can saturate the RPC worker pool while looking like ordinary traffic. Public and semi-public RPC endpoints, and any node fronted by a load balancer that does not cap per-source concurrency, are the exposed surface.

How the attack works

  • Entry point: the eth_call JSON-RPC method (default eth namespace).
  • Preconditions: the RPC endpoint is reachable by the client; no authentication is required.
  • Processing path: eth_call is handled by doCall (internal/ethapi/api.go), which runs the supplied message in the EVM bounded by rpc.gascap (default 50,000,000) and rpc.evmtimeout. When the call target is the BLAKE2F precompile at address 0x09 (core/vm/contracts.go, per EIP-152), the precompile performs rounds compression-function iterations, where rounds is read directly from the first four bytes of the ~213-byte precompile input and priced at one gas per round.
  • Boundary crossed: the caller controls the loop count of a native compression routine executed on the RPC worker, and no per-source rate or concurrency limit is applied at the transport before that work runs.
  • Resulting behaviour: a tiny request drives tens of millions of rounds of real CPU on the server before returning; per-request cost is capped by the gas/timeout ceiling, but the aggregate is that ceiling multiplied by attacker concurrency.

The mechanism is documented method and precompile semantics, not a memory-safety bug: the precompile is doing exactly what EIP-152 specifies, and the pricing (gas = rounds) is the same knob an attacker turns to buy CPU cheaply against an endpoint that meters requests but not per-request compute.

Affected systems

  • Severity: LOW — availability / CPU only (no funds, no consensus break, no authentication bypass), per NR-2026-056.
  • Chain: Ethereum.
  • Implementation: go-ethereum, observed on 1.17.4, with eth_call exposed (the default namespace). The behaviour follows from the documented method plus EIP-152 precompile semantics; it is not 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_call reaching the BLAKE2F precompile (0x09).
  • Not affected: a node that keeps --http / --ws on loopback or behind an authenticating gateway, or one that caps eth_call concurrency per source. The advisory does not establish an affected version range beyond "go-ethereum with eth_call exposed"; the behaviour is the documented method plus EIP-152 semantics, not a version-specific regression.

Evidence

  • Publicly documented: the underlying precompile is specified by EIP-152; the pricing (one gas per round) and the rounds input field are part of that specification. No CVE is claimed — this is availability behaviour of a correctly-implemented method.
  • Independently reproduced by NullRabbit: a reproducer (eth_call_blake2f_precompile_rounds_cpu_amp, family compute_amp, source_class: original) drives the precompile through eth_call 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, a call with rounds = 0x02000000 (≈33.5 million) from a ~585-byte request sustained ≈405 ms of worker CPU, versus ≈1 ms for the same call at rounds = 1. The measurement is lab-fidelity; it demonstrates the linear CPU-for-bytes relationship, not a production-throughput or exploitability claim.
  • Inferred but not reproduced: the aggregate-saturation impact (concurrency × per-request ceiling) follows from the absence of a transport-level per-source cap; the multi-core saturation figure itself is reasoned from the single-request measurement, not separately load-tested here.

Operator actions

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

  • Rate-limit eth_call per source and cap concurrency, not just aggregate request rate — the cost is per request.
  • Tighten rpc.gascap and rpc.evmtimeout for public-facing endpoints to lower the per-request ceiling.
  • 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_call requests whose CPU time is disproportionate to request size, or repeated small calls targeting precompile address 0x09 with large rounds fields. Treat this as a monitoring heuristic, not a validated signature.

Canonical references

Related attacks

  • Same advisory, sibling vector: NR-2026-056 also documents an eth_call + stateOverride tight-JUMP-loop that burns the full rpc.gascap (primitive eth_call_stateoverride_gascap_interpreter_compute_burn). It shares the same root — attacker-influenced synchronous compute on an uncapped RPC worker — but reaches it through the interpreter rather than a precompile, and is tracked as a distinct NRDAX technique.

No other technique links are asserted; related precompile techniques on NRDAX describe different mechanisms (semantics divergence / miscomputation) and are not claimed here.

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

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

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

Related Posts