// @audit sponsor = agreement owner = attackModerator; can forge CORRUPTED with no breach
// @audit ConfidencePoolFactory.sol:82 forces msg.sender == agreement.owner()
// @audit AttackRegistry.sol:793 sets attackModerator = agreementOwner
// @audit markCorrupted requires NO breach proof
ConfidencePoolFactory.createPool forces msg.sender == agreement.owner() at line 82. The BattleChain AttackRegistry sets attackModerator = agreement.owner() at registration. markCorrupted() is onlyAttackModerator and accepts zero breach proof: just the agreement address. The pool sponsor, who controls recoveryAddress, IS the registry's per-agreement attackModerator.
The sponsor can forge CORRUPTED with no real breach, then sweep 100% of staker principal + bonus to their own recoveryAddress via two paths. Slow path: after expiry + 180d, permissionless claimExpired auto-resolves bad-faith CORRUPTED. Fast path: the moderator, following §5's instruction, flags CORRUPTED immediately — no grace period. The only non-sponsor step is DAO approveAttack, which is indistinguishable from honest protocol onboarding. DESIGN.md §6 and §11 do not cover this: §6 assumes a real breach, §11 covers only the DAO-controlled registry pointer, not the per-agreement attackModerator.
The PoC below traces the full attack: stakers are withdraw-trapped after riskWindowStart seals, claimExpired auto-CORRUPTED fires after the grace period, the moderator's SURVIVED rescue is foreclosed by claimsStarted, and 100% of principal reaches recoveryAddress.
The sponsor holds both roles by compile-time invariant (createPool forces msg.sender == agreement.owner(); the registry sets attackModerator = agreementOwner). The only non-sponsor step, DAO approveAttack, is indistinguishable from honest protocol onboarding. Every pool's sponsor IS its agreement's attackModerator.
100% of staker principal + bonus drained to sponsor-controlled recoveryAddress with no real breach. Scales to full TVL of any pool.
Gate the auto-CORRUPTED backstop on a sponsor-independent moderator attestation so a forged registry signal cannot trigger principal sweeps.
Impact- High Every token in the pool leaves: all staker principal plus the entire bonus, sent to an address the sponsor picked and can still change after people have deposited. Stakers have no defense once it starts, because withdraw is dead the moment riskWindowStart seals, and the mechanical resolution sets claimsStarted, which locks the moderator out of any later correction. Likelihood - Very Low The sponsor holds the role by default on every pool, and markCorrupted carries no penalty since it leaves the bond claimable, so arming this is free and unilateral once the DAO has approved the agreement into UNDER_ATTACK. What it still needs is the pool's outcome moderator staying silent for the whole term plus the 180-day grace, and one flagOutcome(SURVIVED) call anywhere in that stretch defeats it outright.
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.