claimExpired gates the auto-CORRUPTED backstop on a lazily-set flag that can legitimately remain zero so a genuinely CORRUPTED agreement can be permanently mis-resolved as EXPIRED, paying stakers in full and denying recoveryAddress its entitled sweepThe auto-CORRUPTED backstop is gated on riskWindowStart != 0, but riskWindowStart is only ever set lazily, inside _observePoolState(), and only when some pool function (stake, contributeBonus, withdraw, flagOutcome, setPoolScope, or the permissionless pokeRiskWindow) happens to be called while the registry is currently UNDER_ATTACK or PROMOTION_REQUESTED.
Nothing guarantees that happens. If the registry transitions straight through (or very quickly through) the active-risk states into CORRUPTED without anyone interacting with the pool in that window — plausible for a low-traffic pool, or if the registry's UNDER_ATTACK→CORRUPTED transition is fast/atomic — then riskWindowStart stays 0 forever, even though the agreement really was attacked and really is CORRUPTED.
So claimExpired() auto-resolves an unresolved pool once block.timestamp >= expiry
Likelihood:
Medium–High. Requires only that no pool entrypoint happened to be called while the registry was in an active-risk state — plausible for a quiet pool, and directly achievable by an attacker who wants to avoid the CORRUPTED sweep, since claimExpired is unauthenticated
Impact:
High. Directly redirects the entire pool's principal + bonus away from recoveryAddress to stakers-at-large, in a case the protocol explicitly designed the CORRUPTED path to handle, and the outcome is unrecoverable once claimsStarted latches.
The following PoC in Foundry shows the mentioned vulnerability.
Decouple the CORRUPTED branch's entry condition from riskWindowStart. The bonus math (_bonusShare) is never invoked on the CORRUPTED path — claimExpired's CORRUPTED branch only flips state and lets claimCorrupted sweep the balance — so there is no bonus-accounting reason to require riskWindowStart != 0
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.