22
33Ethereum client vulnerabilities rated Critical or High severity, drawn from
44across Geth, Besu, Erigon, Reth, Nethermind, Lighthouse, Teku, Nimbus, Lodestar,
5- and Grandine. Three are rated Critical; the rest High. Each entry states the
6- root cause, the attacker action, and the resulting impact.
5+ and Grandine. Each entry states the root cause, the specific attacker input,
6+ and the resulting impact.
7+
8+ Two evidentiary tiers are covered, in two parts:
9+
10+ - ** Part 1 — officially rated.** An advisory, CVE, or GHSA record assigned
11+ the severity itself. 66 entries: 3 Critical, 63 High.
12+ - ** Part 2 — estimated severity.** The fix shipped with no CVE or advisory —
13+ most Ethereum client fixes ship this way. Severity here was inferred from
14+ the fix diff by an LLM classifier, reasoning primarily from client market
15+ share (a bug that can crash a client running >33% of the network is scored
16+ High even with no public disclosure). 110 entries meet that bar, concentrated
17+ in Geth and Lighthouse.
718
819---
920
21+ # Part 1 — Officially rated
22+
1023## Critical
1124
1225- ** [ Besu] CALL / DELEGATECALL gas accounting.** A 32-bit signed/unsigned type
@@ -58,31 +71,38 @@ root cause, the attacker action, and the resulting impact.
5871
5972- Two further entries — "high CPU usage via a crafted p2p message" (CWE-400)
6073 and "crash via a crafted p2p message" (CWE-248) — were reported through the
61- Ethereum Foundation bug bounty. Both advisories shipped a patch with
62- technical details withheld ("more details to be released later").
74+ Ethereum Foundation bug bounty. Both advisories shipped a patch with the
75+ triggering input withheld ("more details to be released later").
6376
6477---
6578
6679## rpc — JSON-RPC / GraphQL surface
6780
6881- ** [ Geth] Unbounded GraphQL query cost.** The GraphQL endpoint had no query
69- complexity or cost limit. With ` --http --graphql ` enabled, an attacker who
70- sent a crafted GraphQL query could exhaust memory and hang the daemon
71- (CWE-400). The vendor's stated position: the GraphQL endpoint was not
72- designed to withstand hostile clients.
82+ complexity or cost limit. Geth's GraphQL schema exposes a recursive
83+ ` parent ` field on ` Block ` , so an attacker who nested that field many levels
84+ deep in one query (` block { parent { parent { parent { ... } } } } ` ) could
85+ force the server to walk arbitrarily far back through ancestor blocks in a
86+ single request, exhausting memory and hanging the daemon (CWE-400, requires
87+ ` --http --graphql ` ). The vendor's stated position: the GraphQL endpoint was
88+ not designed to withstand hostile clients.
7389
7490- ** [ Erigon] Out-of-bounds read in the JSON parser dependency.** A bounds
75- check gap in the ` jsonparser ` dependency let a crafted JSON input trigger an
76- out-of-bounds read (CVE-2026 -32285, CVSS 7.5, CWE-125).
91+ check gap in the ` jsonparser ` dependency let an attacker send malformed
92+ JSON — an input with mismatched or truncated brackets/quotes — that made
93+ the parser's scanner walk past the end of the input buffer instead of
94+ detecting the malformed structure, triggering an out-of-bounds read
95+ (CVE-2026 -32285, CVSS 7.5, CWE-125).
7796
7897- ** [ Geth] Missing block-range validation.** ` TraceChain ` (now
7998 ` debug_traceChain ` ) did not verify that the end block came after the start
80- block. A caller who requested an inverted range could trigger excessive
81- load or abnormal behavior (CWE-20, geth < 1.8.14).
99+ block. A caller who requested a range with the end block before the start
100+ block could trigger excessive load or abnormal behavior (CWE-20, geth <
101+ 1.8.14).
82102
83103- ** [ Reth] Sync-time panic / bad state.** A specific state transition during
84104 live sync could panic the node or leave it in a bad state; the release note
85- does not specify the trigger (v0.1.0-alpha.21).
105+ does not name which state transition triggers it (v0.1.0-alpha.21).
86106
87107---
88108
@@ -102,10 +122,10 @@ root cause, the attacker action, and the resulting impact.
102122
103123- Five further entries are CVEs in ` golang.org/x/crypto/ssh ` (host-key
104124 verification bypass enabling MITM, a nil-pointer panic on the GSSAPI path,
105- a panic on an empty-plaintext packet, and related issues). Geth does not
106- run an SSH server itself; these surfaced through dependency scanning
107- (govulncheck) and their reachability from an actual Ethereum node's attack
108- surface is unconfirmed.
125+ a panic on an empty-plaintext AES-GCM/ChaCha20Poly1305 packet, and related
126+ issues). Geth does not run an SSH server itself; these surfaced through
127+ dependency scanning (govulncheck) and their reachability from an actual
128+ Ethereum node's attack surface is unconfirmed.
109129
110130---
111131
@@ -117,16 +137,19 @@ root cause, the attacker action, and the resulting impact.
117137 victim process OOM-killed (CWE-770), independently in three clients'
118138 libp2p stacks.
119139
120- - ** [ Teku] Netty HTTP/2 resource-management gaps.** The bundled Netty
121- dependency had HTTP/2 resource-limit flaws (CVE-2021 -37136,
122- CVE-2021 -37137); a crafted p2p message / HTTP/2 frame could exhaust
123- resources and cause denial of service.
140+ - ** [ Teku] Netty decompression-bomb DoS.** The bundled Netty dependency's
141+ ` Bzip2Decoder ` and ` SnappyFrameDecoder ` did not cap the size a compressed
142+ stream could expand to when decompressed (CVE-2021 -37136,
143+ CVE-2021 -37137). An attacker who sent a small, highly-compressed bzip2 or
144+ Snappy-framed payload could make Netty expand it into a much larger buffer,
145+ exhausting memory.
124146
125147- ** [ Lighthouse] Outdated ` blst ` cryptography library.** Nodes still on
126- ` <v1.2.0 ` carried a known vulnerability in ` blst ` , the core BLS-signature
148+ ` <v1.2.0 ` carried a known vulnerability in ` blst ` , the BLS-signature
127149 library shared by several consensus clients, tied to the April 2021
128- "Finalized #25 " incident. The advisory does not spell out the exact
129- reproduction steps.
150+ "Finalized #25 " incident — a bug in signature verification reachable
151+ through network-supplied signed consensus data (attestations/blocks). The
152+ advisory does not spell out the exact bypass mechanism.
130153
131154---
132155
@@ -256,4 +279,250 @@ root cause, the attacker action, and the resulting impact.
256279
257280---
258281
282+ # Part 2 — Estimated severity (not officially rated)
283+
284+ None of the entries below carry a CVE or advisory. They are ordinary fix
285+ commits and PRs whose diff and description an LLM classifier judged, after
286+ the fact, to have been High-severity while the bug was live — mainly because
287+ the affected client's market share means a single-node crash there removes
288+ a large enough slice of the network to matter. Confidence on the specific
289+ trigger varies by entry; several PRs carry no more detail than their title.
290+
291+ ## beacon-chain: attestation (Lighthouse)
292+
293+ - ** Slasher OOM via an oversized validator index.** The slasher processed an
294+ attestation's ` validator_index ` before capping it against a sane maximum.
295+ An attacker who submitted a single attestation carrying an artificially
296+ large ` validator_index ` could make the slasher allocate memory
297+ proportional to that index and OOM the node (` PR#9141 ` , high confidence).
298+
299+ - ** Gossip duplicate-cache overrun.** Gossipsub's duplicate-message cache
300+ held only 256 entries. An attacker who got more than 256 validators
301+ to cast attestations for different heads within one slot could exceed the
302+ cache, causing already-seen attestations to be re-propagated and loop
303+ through the network (` PR#832 ` ).
304+
305+ - ** Unbounded slot counter in epoch iteration.** An epoch-slot iterator did
306+ not bound its counter; a slot value close to ` u64::MAX ` sent it into an
307+ infinite loop (` PR#249 ` ).
308+
309+ - ** Reprocess-queue memory leak.** An attestation's entry in the reprocess
310+ queue was only evicted when its * last* attestation timed out. An attacker
311+ who broadcast attestations for random, never-imported block roots left
312+ entries in the queue indefinitely, growing memory without bound (` PR#8065 ` ).
313+
314+ ## bls — BLS signature verification (Lighthouse, Prysm)
315+
316+ - ** [ Prysm] Zero-coefficient bypass in batch signature verification.**
317+ ` VerifyMultipleSignatures ` 's fast batch-verification scheme assigns each
318+ signature a random coefficient ` r_i ` and its correctness depends on every
319+ ` r_i ` being nonzero, but the implementation never checked that. A
320+ coefficient of ` 0 ` drops that signature's contribution from the check
321+ entirely — an attacker who could influence which signature received a
322+ zero coefficient could get an invalid signature accepted alongside valid
323+ ones in the same batch (` ISSUE#9098 ` , CWE-327).
324+
325+ - ** [ Lighthouse] ` is_infinity ` flag computed before branching.** The
326+ aggregate-signature type computed its ` is_infinity ` flag ahead of the
327+ branch that determined which operands were actually being combined, so
328+ aggregating a point-at-infinity signature onto an already-empty aggregate
329+ produced the wrong flag value — a correctness bug reachable during normal
330+ signature aggregation, not attacker input, but one that risked clients
331+ disagreeing on whether an aggregate was valid (` PR#8496 ` ).
332+
333+ ## beacon-chain: block-processing (Lighthouse)
334+
335+ - ** Memory exhaustion from skip-slot fast-forwarding.** Fast-forwarding a
336+ state through skipped slots stored every intermediate state — each several
337+ MB when SSZ-encoded — in RAM. A chain that skipped forward many slots at
338+ once, whether from an absent proposer or a block referencing a far-future
339+ slot, could force the node to hold more multi-megabyte states than it had
340+ memory for (` ISSUE#800 ` , CWE-770).
341+
342+ - ** Balance-check underflow in ` verify_transfer ` .** The (now-removed) Eth1-phase
343+ ` Transfer ` operation checked the sender's balance against ` amount ` alone
344+ instead of ` amount + fee ` ; a transfer whose ` fee ` pushed the true cost
345+ above the balance could underflow that check and be wrongly accepted
346+ (` PR#457 ` ).
347+
348+ - ** Underflow in Eth1 deposit-count bookkeeping** during block processing,
349+ triggerable by a crafted deposit count in an incoming block (` PR#977 ` ).
350+
351+ ## database
352+
353+ - ** [ Geth] Wrong length field in trie-history address lookup.** The
354+ path-based state-history reader validated an address's length against the
355+ wrong field, so a lookup whose address didn't match the expected
356+ account-key length could read out of bounds (` c9009154 ` , CWE-125).
357+
358+ ## fork-choice (Lighthouse)
359+
360+ - ** O(n²) rescans and stack overflow in ` filter_block_tree ` .**
361+ Fork-choice's block-tree filter recursed once per block in the candidate
362+ tree and rescanned all nodes at every step to find each node's children.
363+ On a long, unpruned chain the recursion could overflow the stack around
364+ ~ 30,000 blocks, and even short of that the per-step rescan made processing
365+ cost grow quadratically with chain length (` PR#9090 ` , high confidence).
366+
367+ - ** LMDB cursor-reuse memory corruption in the slasher.** The slasher's
368+ LMDB-backed database returned a reference into a cursor's internal buffer
369+ without copying it; when the caller then deleted that cursor entry, LMDB
370+ was free to overwrite the same memory the caller still held — a bug
371+ present for a long time but only reachable after a later refactor made the
372+ code path executable (` PR#6211 ` ).
373+
374+ ## p2p (Geth)
375+
376+ - ** ` Skip ` -value integer overflow in header requests.** ` GetBlockHeaders `
377+ and the LES ` GetBlockHeadersMsg ` handler both computed ` num + count - 1 `
378+ from a peer-supplied ` Skip ` field without checking for overflow; a request
379+ with a large or negative ` Skip ` could over/underflow that computation and
380+ crash the handler (` e84e13f5 ` , ` 2ea9db06 ` , CWE-190) — the same bug class
381+ the later, officially-rated "Ethereum Packet of Death" advisory fixed for
382+ a different code path in the LES protocol (see Part 1, ` sync ` ).
383+
384+ - ** Discovery self-lookup race.** The node-discovery table could begin a
385+ self-lookup before its own constructor had finished returning the table
386+ reference to the caller; a discovery request racing with node startup
387+ could dereference a nil table (` 78dc88ca ` ).
388+
389+ ## p2p-interface (Lighthouse)
390+
391+ - ** Fork-choice timing attack.** A general timing attack against the
392+ LMD-GHOST fork-choice rule (documented in ` consensus-specs#2101 ` ): an
393+ attacker who controlled * when* , relative to slot boundaries, they
394+ broadcast an attestation could bias which block the network's fork-choice
395+ converged on (` ISSUE#1773 ` , high confidence).
396+
397+ ## rlp (Geth)
398+
399+ - ** Overflow in RLP list-length bounds checking.** RLP list decoding
400+ compared the remaining bytes needed for a nested value against the outer
401+ list's declared size, but that comparison could itself overflow when the
402+ inner size field was large enough. go-fuzz found an input that passed the
403+ check anyway, causing an out-of-bounds read during decoding (` 02b6b045 ` ).
404+
405+ ## serialization
406+
407+ - ** [ Lighthouse] Out-of-range offset in SSZ variable-length list decoding.**
408+ SSZ's variable-length-list decoder read a length-prefix offset from
409+ attacker-controlled bytes without checking it stayed within the buffer. An
410+ out-of-range offset made the decoder try to allocate a billion-element
411+ vector; the failed allocation crashed the node. The author had documented
412+ this exact bug class ("SSZ offset exploits") the year before, but had
413+ missed enforcing it in this decoder (` PR#974 ` , high confidence).
414+
415+ ## state-trie (Geth)
416+
417+ - ** 16-bit overflow in trie parent-reference counting.** Geth's in-memory
418+ trie-pruning mechanism counted, in a ` uint16 ` field, how many parent nodes
419+ referenced each trie node. On a node running with a large cache allowance,
420+ a burst of contract deployments that all referenced the same code hash —
421+ observed in production against Bittrex's wallets — pushed that count past
422+ 65,535, overflowing the counter (` bad60eea ` ).
423+
424+ - ** Race condition on a snapshot diff-layer's ` origin ` field.** The field
425+ was written outside the layer's lock, so a concurrent read of ` origin `
426+ from another goroutine could see a stale or half-written pointer
427+ (` geth:...:PR#22540 ` ).
428+
429+ - ** Deadlock from unlocking on the panic path.** Two code paths in the
430+ state-snapshot iterator returned or panicked without releasing an ` RWLock `
431+ they had already acquired; any panic on those paths — including one
432+ reachable via a crafted transaction — permanently deadlocked the snapshot
433+ for every future caller (` PR#20948 ` ).
434+
435+ ## sync (Geth)
436+
437+ - ** 32-bit index overflow in Ethash DAG generation.** DAG-generation
438+ indexes were computed as 32-bit values. Once the DAG grew large enough for
439+ its size to exceed the 32-bit range — a threshold the chain was on track
440+ to reach at a predictable future block — index arithmetic would wrap and
441+ generate an incorrect DAG, diverging that node from the rest of the
442+ network (` ab4b3b42 ` , CWE-190).
443+
444+ - ** Disproportionate re-hashing via chained ` DELEGATECALL ` .** The VM
445+ re-hashed a contract's bytecode on every call, even though the code was
446+ already stored and addressed by that same hash. A transaction that chained
447+ many ` DELEGATECALL ` s into the same contract forced repeated re-hashing of
448+ identical code, burning CPU disproportionate to the gas paid — one of the
449+ vectors used in the 2016 Shanghai DoS attacks (` 08c1cedc ` ).
450+
451+ - ** Unbounded in-flight header buffer during sync.** The downloader placed
452+ no cap on how many block headers it would hold in memory while fetching
453+ from peers; a sync partner that kept the header queue full could exhaust
454+ the node's memory (` 971ef372 ` ).
455+
456+ ## test (Geth)
457+
458+ - ** Inverted throttle in transaction-announcement flood protection.** The
459+ transaction-announcement fetcher's flood-protection logic kept the
460+ * overflow* amount (` want - maxTxAnnounces ` ) instead of the * allowed*
461+ amount (` maxTxAnnounces - used ` ) when trimming a peer's announcement list
462+ — inverting the intended throttle, so a peer flooding announcements past
463+ the limit triggered a slice-indexing panic instead of being throttled
464+ (` 7dce34f9 ` / ` 61ef89e2 ` , duplicate fixes for the same regression).
465+
466+ - ** Unbounded reorg-log allocation.** Chain reorgs emitted one log object
467+ per removed/added block with no cap; a sufficiently deep reorg could
468+ allocate enough log objects to exhaust memory (` b1c9a13d ` ).
469+
470+ ## transactions (Geth)
471+
472+ - ** Txpool panic on an unvalidated signature.** The transaction pool ran an
473+ account lookup (` GetAccount ` ) against a transaction's sender before
474+ validating that the transaction carried a well-formed signature. An
475+ attacker who handcrafted RLP-encoded transaction bytes with an invalid
476+ signature could panic the pool during that lookup (` PR#195 ` ).
477+
478+ - ** No upper bound on RLP-declared input size** , letting a peer-supplied
479+ message with a crafted length field trigger an integer overflow during
480+ decoding (` 204dd28e ` ).
481+
482+ ## txpool (Geth)
483+
484+ - ** Future transactions could evict funded pending ones.** Before this fix,
485+ an incoming "future" (not-yet-executable) transaction could evict an
486+ already-funded, currently-pending transaction from the pool, and the pool
487+ did not check whether a sender could actually cover an incoming
488+ transaction's cost against funds already committed elsewhere in the pool.
489+ An attacker could exploit either gap to evict other users' transactions or
490+ reserve funds that weren't really available (` 83cccb97 ` , high confidence).
491+
492+ ## gas (Geth)
493+
494+ - ** Wrong threshold in the EVM memory-expansion overflow guard.** The guard
495+ against overflow in memory-expansion gas costs used a threshold too large
496+ for the arithmetic it was protecting: ` 0x7FFFFFFFF ` squared does not
497+ overflow a ` uint64 ` , but a value near ` 0x100000000 ` does. A
498+ memory-expansion request sized just under the guard's threshold could
499+ still overflow the real gas computation (` fef6b529 ` ).
500+
501+ ## evm (Nethermind)
502+
503+ - ** Hardcoded pad direction in zero-padding helper.** A ` UInt256 `
504+ zero-padding helper ignored the caller-specified pad direction on its
505+ out-of-bounds branch and always padded right; call sites that needed
506+ left-padding on that branch received silently wrong byte layouts instead
507+ (` 3d74c0f5 ` ).
508+
509+ ## blobs (Geth)
510+
511+ - ** One bad cell proof dropped an entire Engine API batch.** ` GetBlobs `
512+ aborted its whole response — discarding every blob already collected — the
513+ moment it hit one corrupted or out-of-bounds cell proof while serving an
514+ Engine API request. A single bad blob sidecar sitting in the pool could
515+ deny the whole batch to the consensus client that requested it
516+ (` 4830cc6f ` ).
517+
518+ ## kzg-commitments (Lighthouse)
519+
520+ - ** Unchecked proof size from the paired execution client.** KZG proof-size
521+ handling used an assertion (` .expect() ` ) on a value the execution client
522+ supplied over the Engine API. An execution client that returned an
523+ unexpected proof size — buggy or malicious — could panic the consensus
524+ client (` PR#7957 ` ).
525+
526+ ---
527+
259528Source: ` data/ethereum_vulns.csv ` .
0 commit comments