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.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.