Skip to content

[Security][tentative] Medium (attackable): SSZ State Root Omits Dynamic Epoch Schedule #256

@evonide

Description

@evonide

Context

Summit's consensus state carries a dynamic epoch schedule that maps block heights to epochs after epoch-length protocol changes. Finalizer code uses that schedule to decide epoch-boundary behavior such as last-block handling, pending checkpoint creation, deferred withdrawals, and epoch advancement.

The intended root invariant is that the SSZ state root authenticates every consensus-state field needed by consumers that verify checkpoint state or SSZ proofs. That includes fields that determine which block heights are treated as epoch boundaries.

Claim

The SSZ state root omits the serialized dynamic epoch schedule even though finalizer boundary logic depends on it, so identical roots can represent different height-to-epoch mappings.

A malicious proof provider or root-only state provider can present a state root that authenticates the ordinary SSZ-tree fields while supplying a different serialized dynamic epoch schedule, causing consumers that trust the root for epoch-boundary state to accept unauthenticated schedule behavior.

Flow

The path is reachable after epoch-length protocol updates because those updates mutate DynamicEpocher without changing an SSZ-tree leaf. The start condition is a consumer that relies on the SSZ state root or SSZ proofs to authenticate schedule-dependent state; this should not be read as proof that Summit's normal checkpoint import accepts arbitrary schedule bytes under the same root, because that path also binds raw checkpoint bytes through the checkpoint digest and finalized-header chain.

Impact

Root or proof consumers can authenticate a state root without authenticating the epoch schedule that drives checkpoint timing, deferred withdrawal handling, and epoch advancement. A stale or malicious schedule paired with the same root can cause such consumers to classify heights as different epoch-boundary positions, but the normal checkpoint-import path is not claimed to install that schedule solely from the root.

Root Cause

The dynamic epoch schedule is serialized in ConsensusState and used by finalizer logic, but SszStateTree::rebuild has no field for it and epoch-length updates do not update any committed SSZ leaf.

Code

Related Issues/PRs

Related issues cover adjacent SSZ state-root and proof omissions involving dynamic epoch schedules, boundary mutations, pending state, and proof responses.

Fix

  • Add a committed SSZ leaf or collection root for the dynamic epoch schedule.
  • Update it whenever DynamicEpocher changes.
  • Include schedule proof support if external clients need to verify boundary decisions.
  • Add collision-style tests for states with identical fields but different epoch schedules.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions