Injectable Skill: RPC Surface Audit
L1 trigger: L1_PATTERN=true AND (rpc/ OR jsonrpc OR engine_api OR eth/api OR websocket OR ipc detected in recon subsystem map)
Inject Into: depth-network-surface
Language: Go and Rust
Finding prefix: [RPC-N]
Status: v0.1 draft, Round 4 exemplars pending
Orchestrator Decomposition Guide
- Section 1: depth-network-surface (attack surface)
- Section 2: depth-network-surface (auth + rate limit)
- Section 3: depth-state-trace (subscription state)
- Section 4: depth-edge-case (method boundaries)
When This Skill Activates
Recon identifies an RPC subsystem. RPC is the most publicly exposed attack surface on any L1 node — typically unauthenticated (HTTP JSON-RPC, WebSocket) or semi-authenticated (Engine API with JWT). Even a Medium-severity bug here often upgrades to High because of permissionless reachability (see severity-matrix.md modifier).
1. Attack Surface Enumeration
Enumerate all RPC entry points via LSP workspace/symbol filtered by known method-registration patterns:
| Transport |
Registration pattern |
Example |
| HTTP JSON-RPC |
rpc.Register, httpServer.Handle, #[method(name=...)] |
eth_*, debug_*, admin_* |
| WebSocket |
Same as HTTP + subscription handlers |
eth_subscribe |
| IPC (unix socket) |
Often same as HTTP |
Geth admin namespace |
| Engine API |
JWT-authenticated |
engine_newPayloadV*, engine_forkchoiceUpdatedV* |
| Prometheus metrics |
/metrics endpoint |
often bound to all interfaces |
| PPROF profiling |
/debug/pprof/ (Go) |
should NEVER be public |
Write the enumeration to scratchpad/rpc_surface.md.
2. Authentication and Namespace Gating
2a. Default exposure
- What namespaces are enabled by default on HTTP? (Geth:
eth, net, web3. Not admin, debug, personal.)
- Is the default bind address localhost or 0.0.0.0?
- Is authentication required for any namespace?
2b. Dangerous namespaces
admin_* — should never be exposed on HTTP; check the config validation
debug_* — can expose internal state; check default
personal_* — key management; deprecated in Geth but still present in forks
engine_* — JWT-authenticated; check the JWT secret handling
miner_* / txpool_* — varies by client
2c. CORS and origin checks
- Is the WebSocket origin-checked? (Yes in Geth; verify in forks)
- Are CORS headers tight? Default should be same-origin
2d. JWT handling (Engine API specifically)
- JWT secret rotation: what happens on secret change mid-operation?
- JWT
iat claim: is clock drift tolerated? Excessive tolerance is a replay window
- JWT algorithm whitelisting: must be HS256 only; reject
alg=none and key-confusion attacks
Tag: [RPC-AUTH:{issue}]
3. Rate Limiting and Resource Quotas
3a. Per-request cost tracking
Some RPC methods are cheap (eth_blockNumber), others are expensive (eth_getLogs with wide range, debug_traceTransaction). Is there a cost model that bounds work per request?
Check:
- For each expensive method, is there a result size limit? E.g.,
eth_getLogs with max_logs_per_request
- For each historical query, is there a block range limit? E.g.,
eth_getLogs with fromBlock → toBlock span
- For each trace method, is there a trace depth / trace time limit?
3b. Per-client rate limit
- Is there rate limiting by IP, by API key, by connection?
- What's the default burst + sustained rate?
3c. Total concurrent request cap
- Maximum simultaneous HTTP requests
- Maximum simultaneous WebSocket connections
- Maximum subscriptions per WebSocket connection
Tag: [RPC-RATE:{scope}:{limit-or-unbounded}]
3d. Outbound HTTP client timeout audit
The skill historically focused on incoming RPC. Audit OUTBOUND HTTP clients used by the node for peer fetches and inter-node API calls — they are a symmetric DoS vector.
Check:
- For each
reqwest::Client::builder(), awc::Client::builder(), hyper::Client::builder(), and http::Client::new() site, verify .timeout(...) AND .connect_timeout(...) are set explicitly. The library defaults are NO timeout.
- For each
tokio::time::timeout wrapper around an outbound call, verify the timeout bound is a constant (not derived from peer-controlled input).
- For Go:
http.Client{} literal (zero value) has NO timeout — flag every site that constructs http.Client without setting Timeout. Also check Transport.DialContext for connect-timeout.
- Fail mode: an unresponsive remote peer can hang the calling task forever, blocking critical bootstrap, sync, or peer-handshake routines. Combined with peer-list flooding, an attacker can stall every honest node's startup.
Tag: [RPC-CLIENT-NO-TIMEOUT:{file}:{line}]
3e. JSON number precision for u64 fields
JavaScript clients (and many other dynamic languages) cannot represent integers above 2^53 - 1 precisely. L1 nodes that serialize u64 fields as JSON numbers (not strings) will silently corrupt block heights, balances, gas values, and timestamps when consumed by JS clients.
Check:
- For each
serde::Serialize impl on a struct exposed via RPC, find every u64 / u128 / i64 / i128 field
- Verify the serializer either uses
#[serde(with = "as_string")] or a similar string-coercion attribute, OR that the field's max value provably fits in 53 bits
- For Go: check
json.Marshal of uint64 fields and *hexutil.Uint64 wrappers (the latter is correct, the former is not)
Tag: [RPC-JSON-PRECISION:{type}.{field}]
3f. Success Return vs Side-Effect Completion
For each RPC handler, peer HTTP endpoint, and outbound API client wrapper that returns Ok(()), HTTP 2xx, a success JSON body, or increments peer score, verify that the underlying side effect completed first.
Check:
- A handler must not return success when validation failed, delivery failed, or the requested state transition was skipped.
- Outbound calls must propagate transport errors, non-2xx status, timeout, parse failure, and body-delivery failure before any success return or peer-score reward.
- For each
Ok(()), StatusCode::OK, HttpResponse::Ok, or equivalent success path, trace the immediately preceding awaited call and ensure it is guarded by ?, explicit error mapping, or a checked status branch.
- For Go, check
return nil paths after Do(req), Write, Encode, channel send, or peer delivery; success must be after the side effect, not before it.
Tag: [RPC-OK-BEFORE-EFFECT:{file}:{line}]
4. Subscription Buffer Overflows
WebSocket subscriptions are a classic DoS vector: slow consumer, fast producer.
Check:
- For each
eth_subscribe type (newHeads, logs, newPendingTransactions, syncing), what is the outbound buffer depth per subscriber?
- When the buffer fills, does the server drop messages, kill the subscription, or block the producer?
- Can a slow consumer cause back-pressure on the chain head update path? (Very bad — affects consensus.)
Tag: [RPC-SUB:{type}:{buffer-policy}]
5. Per-Method Deep Checks
5a. eth_getLogs
- Block range validation
- Bloom filter efficiency on wide ranges
- Empty-log edge case
- Topic filter malformed input
5b. debug_traceTransaction / debug_traceBlock
- Opcode count bound
- Memory bound per trace
- Timeout per trace
- Tracer code injection (for custom tracers)
5c. eth_call / eth_estimateGas
- Gas cap per call
- State override safety (can caller manipulate state?)
- Call depth limit
5d. eth_getProof
- Proof size for a deeply nested account
- Empty account edge case
5e. engine_newPayload (Engine API)
- Payload validation: any panic path?
- State diff computation: unbounded?
- Concurrent newPayload races: handled?
6. Panic Surfaces
An RPC method that panics crashes the node. The Go standard library recovers in HTTP handlers by default, but custom dispatch may not.
Check:
- Is there a top-level
recover / panic catcher?
- For each handler, trace panic paths:
panic!(), .unwrap() on user input, integer division, out-of-bounds access
- For Rust:
.unwrap() and .expect() on decoded user input are panic-on-attack
Tag: [RPC-PANIC:{method}:{path}]
6a. Admin-endpoint Exposure + Key/Secret Leakage
Privileged and operator-facing RPC surfaces are a distinct, high-value class from public read methods. The failure modes: an admin/debug namespace reachable without authentication, an expensive trace/debug method exposed without auth or rate limit, or a response body that echoes a secret (private key, JWT, mnemonic, node identity key).
Bounded reads: read SCIP graph artifacts (caller_map.md, callee_map.md, state_write_map.md, function_summary.md) to find privileged route registrations and their auth-gate callers; on-demand single-symbol source reads for the dispatcher and individual privileged handlers only; never bulk-read large files.
Heuristics:
- Grep for privileged route names:
admin_*, debug_*, personal_*, miner_*, txpool_content, engine_*, and registration of any handler whose name implies mutation (setEtherbase, addPeer, removePeer, importRawKey, unlockAccount, startMining, stop, shutdown, nodeInfo).
- Auth gate: for EACH privileged handler, trace to its dispatcher and verify an authentication/authorization check runs BEFORE the handler body. A privileged method registered on the same HTTP listener as public methods, with no per-method gate, is a finding. Confirm the default
--http.api / enabled-namespace list does NOT include privileged namespaces.
- Cost without auth: any unauthenticated method that performs a trace, full-state walk, or unbounded historical scan (
debug_traceBlockByNumber, custom debug_*) without a rate limit is a DoS finding even if it is "read-only".
- Secret in response: grep handler return paths for fields named
private_key, secret, jwt, mnemonic, seed_phrase, keystore, enode with embedded key material. Verify no handler serializes a secret into a JSON body, error message, or log line returned to the caller. nodeInfo/enode exposure of the node identity key is a finding.
- Bind address: verify privileged transports (IPC for
admin, Engine API) are not bound to 0.0.0.0 by default.
Severity: unauthenticated admin/key-management method reachable over HTTP is Critical (remote node takeover); unauthenticated expensive debug method is High (DoS); secret echoed in a response is Critical.
Tag: [RPC-ADMIN-NOAUTH:{method}], [RPC-SECRET-LEAK:{method}.{field}]
7. Boundary conditions
| State |
Test |
Expected |
Observed |
| Empty params |
method with empty params |
spec-defined (often error) |
|
| Huge params |
10 MB of JSON in request |
rejected before parsing |
|
| Deeply nested JSON |
1000-level nesting |
rejected (stack overflow guard) |
|
| Unknown method |
method not registered |
error, not crash |
|
| Concurrent duplicate sub |
same subscription twice |
spec-defined |
|
| getLogs 10M blocks |
wide range |
rejected |
|
8. Output schema
- Layer: rpc
- Bug class: auth-bypass / rate-limit-missing / subscription-dos / method-dos / panic-surface
- Preferred evidence tags:
[FUZZ-PASS] > [LSP-TRACE] > [CODE-TRACE]
- Severity baseline: Medium-High. Panic-reachable-from-unauthenticated = High or Critical (permissionless node crash)
9. Known bug exemplars (v0.2 — Round 4 verified)
Geth GraphQL DoS (CVE-2023-42319, GHSA-v9jh-j8px-98vq) — with --http --graphql enabled, a crafted GraphQL query consumed memory and hung the daemon. Fixed in v1.13.5. GHSA-v9jh-j8px-98vq. Skill catch point: Section 3a — every user-controlled query parser needs query depth cap, memory cap per request, total time cap, allocated-bytes cap.
Geth CVE-2025-24883 p2p handshake invalid EC point — RLPx handshake accepted an all-zero secp256k1 public key without validating it lies on the curve. ECDH math with the bogus point produced undefined behavior and DoS. DailyCVE. Methodology nuance: the p2p handshake is part of the RPC attack surface, not a separate domain. Any attacker-supplied curve point must be validated on-curve AND in prime-order subgroup before any math.
Geth snap/1 trie-node nil-deref panic (CVE-2021-41173, GHSA-59hh-656j-3p7v) — trie.TryGetNode returns nil for child of fullnode when path exhausted; caller dereferenced without a nil check → origNode.cache() panic. Any malicious snap peer could kill any node. GHSA-59hh-656j-3p7v. Skill catch point: Section 6 (Panic surfaces) — every p2p message handler must never panic. Each .unwrap(), nil-deref, panic-on-slice-index, uncovered switch is a finding.
Engine API JWT replay / time-skew exploitation — the Engine API spec allows ±60s iat window. Implementations that fail to enforce freshness allow JWT reuse across rotation boundaries. execution-apis auth spec. Skill catch point: Section 2d — hard iat window, no clock-jump tolerance, rotation-on-restart, no persistence across process boundaries.
Methodology nuance from Round 4 (handshake is RPC surface)
Add to Section 1: The attack surface enumeration MUST include p2p handshake and pre-auth message handlers, not just JSON-RPC methods. CVE-2025-24883 was classified in the RPC surface because the handshake is the first code that touches untrusted input. Cross-reference with p2p-dos-and-eclipse Section 2f (pre-auth panic check).
10. Fallback if primitives unavailable
- Grep for
"eth_" / "debug_" / "admin_" method names
- Find the dispatch table / method registration
- Read the top-level HTTP handler for auth gate
Cross-references
- Related:
mempool-asymmetric-dos (eth_sendRawTransaction is an insertion), execution-client-hardening (debug methods touch VM)
- Consumed by:
depth-network-surface
- Severity:
docs/l1-mode/severity-matrix.md
1---2name: rpc-surface-audit3description: L1 trigger - audits JSON-RPC and Engine API surfaces for authentication bypass, rate limiting, subscription buffer overflows, and method-specific DoS.4---56# Injectable Skill: RPC Surface Audit78> **L1 trigger**: `L1_PATTERN=true` AND (`rpc/` OR `jsonrpc` OR `engine_api` OR `eth/api` OR `websocket` OR `ipc` detected in recon subsystem map)9> **Inject Into**: `depth-network-surface`10> **Language**: Go and Rust11> **Finding prefix**: `[RPC-N]`12> **Status**: v0.1 draft, Round 4 exemplars pending1314## Orchestrator Decomposition Guide1516- Section 1: depth-network-surface (attack surface)17- Section 2: depth-network-surface (auth + rate limit)18- Section 3: depth-state-trace (subscription state)19- Section 4: depth-edge-case (method boundaries)2021## When This Skill Activates2223Recon identifies an RPC subsystem. RPC is the most publicly exposed attack surface on any L1 node — typically unauthenticated (HTTP JSON-RPC, WebSocket) or semi-authenticated (Engine API with JWT). Even a Medium-severity bug here often upgrades to High because of permissionless reachability (see severity-matrix.md modifier).2425## 1. Attack Surface Enumeration2627Enumerate all RPC entry points via LSP `workspace/symbol` filtered by known method-registration patterns:2829| Transport | Registration pattern | Example |30|---|---|---|31| **HTTP JSON-RPC** | `rpc.Register`, `httpServer.Handle`, `#[method(name=...)]` | `eth_*`, `debug_*`, `admin_*` |32| **WebSocket** | Same as HTTP + subscription handlers | `eth_subscribe` |33| **IPC** (unix socket) | Often same as HTTP | Geth `admin` namespace |34| **Engine API** | JWT-authenticated | `engine_newPayloadV*`, `engine_forkchoiceUpdatedV*` |35| **Prometheus metrics** | `/metrics` endpoint | often bound to all interfaces |36| **PPROF profiling** | `/debug/pprof/` (Go) | should NEVER be public |3738Write the enumeration to `scratchpad/rpc_surface.md`.3940## 2. Authentication and Namespace Gating4142### 2a. Default exposure43- What namespaces are enabled by default on HTTP? (Geth: `eth`, `net`, `web3`. Not `admin`, `debug`, `personal`.)44- Is the default bind address localhost or 0.0.0.0?45- Is authentication required for any namespace?4647### 2b. Dangerous namespaces48- `admin_*` — should never be exposed on HTTP; check the config validation49- `debug_*` — can expose internal state; check default50- `personal_*` — key management; deprecated in Geth but still present in forks51- `engine_*` — JWT-authenticated; check the JWT secret handling52- `miner_*` / `txpool_*` — varies by client5354### 2c. CORS and origin checks55- Is the WebSocket origin-checked? (Yes in Geth; verify in forks)56- Are CORS headers tight? Default should be same-origin5758### 2d. JWT handling (Engine API specifically)59- JWT secret rotation: what happens on secret change mid-operation?60- JWT `iat` claim: is clock drift tolerated? Excessive tolerance is a replay window61- JWT algorithm whitelisting: must be HS256 only; reject `alg=none` and key-confusion attacks6263Tag: `[RPC-AUTH:{issue}]`6465## 3. Rate Limiting and Resource Quotas6667### 3a. Per-request cost tracking68Some RPC methods are cheap (`eth_blockNumber`), others are expensive (`eth_getLogs` with wide range, `debug_traceTransaction`). Is there a cost model that bounds work per request?6970**Check**:71- For each expensive method, is there a **result size limit**? E.g., `eth_getLogs` with `max_logs_per_request`72- For each historical query, is there a **block range limit**? E.g., `eth_getLogs` with `fromBlock` → `toBlock` span73- For each trace method, is there a **trace depth** / **trace time** limit?7475### 3b. Per-client rate limit76- Is there rate limiting by IP, by API key, by connection?77- What's the default burst + sustained rate?7879### 3c. Total concurrent request cap80- Maximum simultaneous HTTP requests81- Maximum simultaneous WebSocket connections82- Maximum subscriptions per WebSocket connection8384Tag: `[RPC-RATE:{scope}:{limit-or-unbounded}]`8586## 3d. Outbound HTTP client timeout audit8788The skill historically focused on incoming RPC. Audit OUTBOUND HTTP clients used by the node for peer fetches and inter-node API calls — they are a symmetric DoS vector.8990**Check**:91- For each `reqwest::Client::builder()`, `awc::Client::builder()`, `hyper::Client::builder()`, and `http::Client::new()` site, verify `.timeout(...)` AND `.connect_timeout(...)` are set explicitly. The library defaults are NO timeout.92- For each `tokio::time::timeout` wrapper around an outbound call, verify the timeout bound is a constant (not derived from peer-controlled input).93- For Go: `http.Client{}` literal (zero value) has NO timeout — flag every site that constructs `http.Client` without setting `Timeout`. Also check `Transport.DialContext` for connect-timeout.94- **Fail mode**: an unresponsive remote peer can hang the calling task forever, blocking critical bootstrap, sync, or peer-handshake routines. Combined with peer-list flooding, an attacker can stall every honest node's startup.9596Tag: `[RPC-CLIENT-NO-TIMEOUT:{file}:{line}]`9798## 3e. JSON number precision for u64 fields99100JavaScript clients (and many other dynamic languages) cannot represent integers above `2^53 - 1` precisely. L1 nodes that serialize `u64` fields as JSON numbers (not strings) will silently corrupt block heights, balances, gas values, and timestamps when consumed by JS clients.101102**Check**:103- For each `serde::Serialize` impl on a struct exposed via RPC, find every `u64` / `u128` / `i64` / `i128` field104- Verify the serializer either uses `#[serde(with = "as_string")]` or a similar string-coercion attribute, OR that the field's max value provably fits in 53 bits105- For Go: check `json.Marshal` of `uint64` fields and `*hexutil.Uint64` wrappers (the latter is correct, the former is not)106107Tag: `[RPC-JSON-PRECISION:{type}.{field}]`108109## 3f. Success Return vs Side-Effect Completion110111For each RPC handler, peer HTTP endpoint, and outbound API client wrapper that returns `Ok(())`, HTTP 2xx, a success JSON body, or increments peer score, verify that the underlying side effect completed first.112113**Check**:114- A handler must not return success when validation failed, delivery failed, or the requested state transition was skipped.115- Outbound calls must propagate transport errors, non-2xx status, timeout, parse failure, and body-delivery failure before any success return or peer-score reward.116- For each `Ok(())`, `StatusCode::OK`, `HttpResponse::Ok`, or equivalent success path, trace the immediately preceding awaited call and ensure it is guarded by `?`, explicit error mapping, or a checked status branch.117- For Go, check `return nil` paths after `Do(req)`, `Write`, `Encode`, channel send, or peer delivery; success must be after the side effect, not before it.118119Tag: `[RPC-OK-BEFORE-EFFECT:{file}:{line}]`120121## 4. Subscription Buffer Overflows122123WebSocket subscriptions are a classic DoS vector: slow consumer, fast producer.124125**Check**:126- For each `eth_subscribe` type (newHeads, logs, newPendingTransactions, syncing), what is the outbound buffer depth per subscriber?127- When the buffer fills, does the server drop messages, kill the subscription, or block the producer?128- Can a slow consumer cause back-pressure on the chain head update path? (Very bad — affects consensus.)129130Tag: `[RPC-SUB:{type}:{buffer-policy}]`131132## 5. Per-Method Deep Checks133134### 5a. `eth_getLogs`135- Block range validation136- Bloom filter efficiency on wide ranges137- Empty-log edge case138- Topic filter malformed input139140### 5b. `debug_traceTransaction` / `debug_traceBlock`141- Opcode count bound142- Memory bound per trace143- Timeout per trace144- Tracer code injection (for custom tracers)145146### 5c. `eth_call` / `eth_estimateGas`147- Gas cap per call148- State override safety (can caller manipulate state?)149- Call depth limit150151### 5d. `eth_getProof`152- Proof size for a deeply nested account153- Empty account edge case154155### 5e. `engine_newPayload` (Engine API)156- Payload validation: any panic path?157- State diff computation: unbounded?158- Concurrent newPayload races: handled?159160## 6. Panic Surfaces161162An RPC method that panics crashes the node. The Go standard library recovers in HTTP handlers by default, but custom dispatch may not.163164**Check**:165- Is there a top-level `recover` / panic catcher?166- For each handler, trace panic paths: `panic!()`, `.unwrap()` on user input, integer division, out-of-bounds access167- For Rust: `.unwrap()` and `.expect()` on decoded user input are panic-on-attack168169Tag: `[RPC-PANIC:{method}:{path}]`170171## 6a. Admin-endpoint Exposure + Key/Secret Leakage172173Privileged and operator-facing RPC surfaces are a distinct, high-value class from public read methods. The failure modes: an admin/debug namespace reachable without authentication, an expensive trace/debug method exposed without auth or rate limit, or a response body that echoes a secret (private key, JWT, mnemonic, node identity key).174175**Bounded reads**: read SCIP graph artifacts (`caller_map.md`, `callee_map.md`, `state_write_map.md`, `function_summary.md`) to find privileged route registrations and their auth-gate callers; on-demand single-symbol source reads for the dispatcher and individual privileged handlers only; never bulk-read large files.176177**Heuristics**:1781. Grep for privileged route names: `admin_*`, `debug_*`, `personal_*`, `miner_*`, `txpool_content`, `engine_*`, and registration of any handler whose name implies mutation (`setEtherbase`, `addPeer`, `removePeer`, `importRawKey`, `unlockAccount`, `startMining`, `stop`, `shutdown`, `nodeInfo`).1792. **Auth gate**: for EACH privileged handler, trace to its dispatcher and verify an authentication/authorization check runs BEFORE the handler body. A privileged method registered on the same HTTP listener as public methods, with no per-method gate, is a finding. Confirm the default `--http.api` / enabled-namespace list does NOT include privileged namespaces.1803. **Cost without auth**: any unauthenticated method that performs a trace, full-state walk, or unbounded historical scan (`debug_traceBlockByNumber`, custom `debug_*`) without a rate limit is a DoS finding even if it is "read-only".1814. **Secret in response**: grep handler return paths for fields named `private_key`, `secret`, `jwt`, `mnemonic`, `seed_phrase`, `keystore`, `enode` with embedded key material. Verify no handler serializes a secret into a JSON body, error message, or log line returned to the caller. `nodeInfo`/`enode` exposure of the node identity key is a finding.1825. **Bind address**: verify privileged transports (IPC for `admin`, Engine API) are not bound to `0.0.0.0` by default.183184**Severity**: unauthenticated admin/key-management method reachable over HTTP is Critical (remote node takeover); unauthenticated expensive debug method is High (DoS); secret echoed in a response is Critical.185186Tag: `[RPC-ADMIN-NOAUTH:{method}]`, `[RPC-SECRET-LEAK:{method}.{field}]`187188## 7. Boundary conditions189190| State | Test | Expected | Observed |191|---|---|---|---|192| Empty params | method with empty params | spec-defined (often error) | |193| Huge params | 10 MB of JSON in request | rejected before parsing | |194| Deeply nested JSON | 1000-level nesting | rejected (stack overflow guard) | |195| Unknown method | method not registered | error, not crash | |196| Concurrent duplicate sub | same subscription twice | spec-defined | |197| getLogs 10M blocks | wide range | rejected | |198199## 8. Output schema200201- **Layer**: rpc202- **Bug class**: auth-bypass / rate-limit-missing / subscription-dos / method-dos / panic-surface203- **Preferred evidence tags**: `[FUZZ-PASS]` > `[LSP-TRACE]` > `[CODE-TRACE]`204- **Severity baseline**: Medium-High. Panic-reachable-from-unauthenticated = High or Critical (permissionless node crash)205206## 9. Known bug exemplars (v0.2 — Round 4 verified)2072081. **Geth GraphQL DoS (CVE-2023-42319, GHSA-v9jh-j8px-98vq)** — with `--http --graphql` enabled, a crafted GraphQL query consumed memory and hung the daemon. Fixed in v1.13.5. [GHSA-v9jh-j8px-98vq](https://github.com/advisories/GHSA-v9jh-j8px-98vq). **Skill catch point**: Section 3a — every user-controlled query parser needs query depth cap, memory cap per request, total time cap, allocated-bytes cap.2092102. **Geth CVE-2025-24883 p2p handshake invalid EC point** — RLPx handshake accepted an all-zero secp256k1 public key without validating it lies on the curve. ECDH math with the bogus point produced undefined behavior and DoS. [DailyCVE](https://dailycve.com/go-ethereum-denial-of-service-via-malicious-p2p-message-cve-2025-24883-moderate/). **Methodology nuance**: **the p2p handshake is part of the RPC attack surface**, not a separate domain. Any attacker-supplied curve point must be validated on-curve AND in prime-order subgroup before any math.2112123. **Geth snap/1 trie-node nil-deref panic (CVE-2021-41173, GHSA-59hh-656j-3p7v)** — `trie.TryGetNode` returns nil for child of fullnode when path exhausted; caller dereferenced without a nil check → `origNode.cache()` panic. Any malicious snap peer could kill any node. [GHSA-59hh-656j-3p7v](https://github.com/ethereum/go-ethereum/security/advisories/GHSA-59hh-656j-3p7v). **Skill catch point**: Section 6 (Panic surfaces) — every p2p message handler must never panic. Each `.unwrap()`, nil-deref, panic-on-slice-index, uncovered switch is a finding.2132144. **Engine API JWT replay / time-skew exploitation** — the Engine API spec allows ±60s iat window. Implementations that fail to enforce freshness allow JWT reuse across rotation boundaries. [execution-apis auth spec](https://github.com/ethereum/execution-apis/blob/main/src/engine/authentication.md). **Skill catch point**: Section 2d — hard iat window, no clock-jump tolerance, rotation-on-restart, no persistence across process boundaries.215216### Methodology nuance from Round 4 (handshake is RPC surface)217218**Add to Section 1**: The attack surface enumeration MUST include **p2p handshake and pre-auth message handlers**, not just JSON-RPC methods. CVE-2025-24883 was classified in the RPC surface because the handshake is the first code that touches untrusted input. Cross-reference with `p2p-dos-and-eclipse` Section 2f (pre-auth panic check).219220## 10. Fallback if primitives unavailable221222- Grep for `"eth_"` / `"debug_"` / `"admin_"` method names223- Find the dispatch table / method registration224- Read the top-level HTTP handler for auth gate225226## Cross-references227228- Related: `mempool-asymmetric-dos` (eth_sendRawTransaction is an insertion), `execution-client-hardening` (debug methods touch VM)229- Consumed by: `depth-network-surface`230- Severity: `docs/l1-mode/severity-matrix.md`