A single post-expiry pokeRiskWindow seals riskWindowStart = expiry for out-of-term risk, satisfying the permissionless auto-CORRUPTED gate and sweeping every staker's principal to recoveryAddress instead of returning it.
When a pool's agreement was benign for the entire term (riskWindowStart == 0) and only becomes CORRUPTED after expiry, the permissionless claimExpired backstop must fall through to EXPIRED and return principal. The auto-CORRUPTED branch is gated on riskWindowStart != 0 precisely so unobserved / out-of-term risk cannot trigger a scope-blind principal sweep (DESIGN §6).
pokeRiskWindow has no block.timestamp < expiry guard, so a griefer can seal riskWindowStart = expiry from a transient post-expiry active-risk moment. That satisfies the auto-CORRUPTED gate; after expiry + MODERATOR_CORRUPTED_GRACE anyone finalizes the pool as bad-faith CORRUPTED, sweeping all principal to recoveryAddress. Absent the poke, riskWindowStart stays 0 and principal is returned. This falsifies DESIGN §6's claim that "a poke does not manufacture it."
Likelihood:
Triggers when a pool's agreement stays benign through the term (riskWindowStart == 0), the moderator does not flag within the 180-day post-expiry grace (the documented moderator-absence backstop this path exists for), and the agreement reaches CORRUPTED after expiry.
Realized by a single cheap pokeRiskWindow transaction sent during any transient post-expiry active-risk moment, before the first claimExpired.
Impact:
All staker principal is swept to recoveryAddress instead of returned — a full refund is converted into a total loss for every staker.
The griefer overrides the conservative, staker-favorable EXPIRED default the design deliberately chose for the no-observed-risk backstop.
Identical facts (benign entire term, CORRUPTED after expiry). The exploit adds one post-expiry pokeRiskWindow that seals riskWindowStart = expiry, satisfying the auto-CORRUPTED gate so claimExpired sweeps all principal to recoveryAddress. The control omits the poke: riskWindowStart stays 0, claimExpired resolves EXPIRED, principal is returned. One griefer tx is the difference between total loss and full refund.
Same root cause as the bonus-redirect finding — stop opening the risk window after the term ends:
With this guard, a post-expiry agreement that was benign in-term keeps riskWindowStart == 0, so claimExpired correctly resolves EXPIRED and returns principal.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.