Skip to content

Commit d4c1b42

Browse files
docs: sharpen trigger specifics and add estimated-severity findings
Fix vague "crafted input" phrasing in the official-severity section (name the actual field/value/query shape) and correct a wrong mechanism for the Teku Netty CVEs (decompression bomb, not HTTP/2 frames). Add a second part covering the 110 rows where severity_estimated is Critical/High but no CVE/advisory exists — the LLM-inferred, market- share-driven findings not previously included. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 89844e7 commit d4c1b42

1 file changed

Lines changed: 293 additions & 24 deletions

File tree

docs/critical_high_findings.md

Lines changed: 293 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@
22

33
Ethereum client vulnerabilities rated Critical or High severity, drawn from
44
across 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+
259528
Source: `data/ethereum_vulns.csv`.

0 commit comments

Comments
 (0)