Normal behavior: a pool's outcome is a moderator judgement that the protocol treats as provisional until the first claim. docs/DESIGN.md §4 provides a pre-claim outcome-correction window precisely so the moderator can "fix a typo'd outcome/attacker before any participant locks in the wrong distribution". A pool outcome can legitimately differ from the registry: per §8, "if the agreement is CORRUPTED but the vulnerability fell outside this pool's scope, the moderator flags SURVIVED and stakers recover stake + bonus." The design also treats a scope-blind bad-faith CORRUPTED sweep as over-punishment that is acceptable only as a delayed last-resort backstop, not an immediate action (§6).
Specific issue: claimCorrupted() is permissionless (no msg.sender check) and latches claimsStarted when it executes. After a mistaken bad-faith CORRUPTED flag — the exact class of mistake the documented §4 correction window exists to fix — any address can call claimCorrupted before the moderator's correction lands, sweeping the entire pool (stake + bonus) to recoveryAddress and latching finality. The moderator's documented §4 correction to SURVIVED (the §8 out-of-scope reclassification) then reverts with OutcomeAlreadySet. Stakers whose in-scope contracts actually survived lose their entire principal + bonus. Two documented design properties are broken at once: the §4 pre-claim correction window is foreclosed by an unprivileged caller, and the §6 "delayed backstop only" rule is violated because the scope-blind sweep executes immediately, with no correction delay, against a present and willing moderator.
Root cause (src/ConfidencePool.sol):
This is not an attacker-recipient correction issue and not a trusted-role failure. The harmful caller does not need to be the named attacker or any participant — any address executes claimCorrupted. The affected party is the entire staker set, because the whole pool is swept to recoveryAddress. The defeated protections are the moderator's documented outcome correction (bad-faith CORRUPTED → SURVIVED, §4/§8) and the §6 "delayed backstop" rule; the value is captured by the sponsor-controlled recoveryAddress. Restricting the caller is not a fix — the recipient party is economically incentivized to keep the swept pool; the missing control is a correction delay before finalization.
Likelihood: Medium
The precondition is a design-anticipated state, not an unmodeled edge case. The protocol ships an entire pre-claim correction window (§4) because it explicitly expects outcomes to need correcting before the first claim, and treats the initial flag as provisional. A mistaken bad-faith CORRUPTED that the moderator intends to correct is therefore inside the protocol's normal operating envelope — the design itself budgets for it.
Once that modeled state exists, exploitation is immediate, permissionless, and capital-free. claimCorrupted() has no role gate, no timing skill, and no capital requirement; a single call finalizes the sweep before the moderator's correction transaction can be mined.
The recipient recoveryAddress is sponsor-controlled, so the sweep has a standing beneficiary with a direct economic incentive to foreclose any correction that would return funds to stakers. The exploiting party is not a hypothetical random griefer but an incentivized actor watching for exactly this state — pushing the conditional probability of exploitation, given the modeled precondition, close to 1.
This is why it is not Low: the failure is not "a trusted moderator errs." The moderator is trusted for the final judgement, and the design deliberately separates the provisional first flag from the final outcome via §4. The defect is that the protocol's own correction mechanism is bypassable by an unprivileged, incentivized actor — a design-anticipated condition with trivial execution by anyone.
Impact: High
The entire pool — every staker's principal plus the bonus — is irreversibly swept to recoveryAddress. When the correct outcome was SURVIVED (out-of-scope breach, §8), stakers whose contracts survived lose 100% of their deposit.
The moderator's documented §4 correction cannot be applied — it reverts with OutcomeAlreadySet. The value is captured by the sponsor-controlled recoveryAddress, so an unprivileged call directly enriches the sponsor at the entire staker set's expense.
Add as test/poc/CorruptedCorrectionForeclosed.t.sol; run forge test --match-path test/poc/CorruptedCorrectionForeclosed.t.sol -vvv. Forward-only, reachable registry transitions; the harmful caller (griefer) holds no role.
Output:
Do not let a permissionless call finalize a bad-faith CORRUPTED outcome inside the §4 correction window. Gate claimCorrupted (and the value movement) behind the same correction delay the moderator needs, so a correction can still be applied before funds leave — this also restores the §6 "delayed backstop" property:
Alternatively, make flagOutcome for bad-faith CORRUPTED two-phase: flag first, and allow the sweep only after a correction delay has elapsed — so the moderator can re-flag out of a mistaken bad-faith CORRUPTED while no value has yet moved. (Restricting the caller to recoveryAddress/sponsor is not recommended: that party has the economic incentive to keep the swept pool.)
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.