Normal behavior: The pool freezes its account scope permanently the first time it observes the registry past pre-attack staging, so existing stakers keep underwriting exactly the account list they accepted; only a successful stake()/contributeBonus() (or a lock path that does not revert) persists that scopeLocked flag.
Specific issue: When the first post-staging state observed is ATTACK_REQUESTED, both public lock paths (setPoolScope, pokeRiskWindow) set scopeLocked and then revert, rolling it back to false, and the sponsor suppresses the deposit paths with pause(). The registry's ordinary soft rejection returns the agreement ATTACK_REQUESTED -> NOT_DEPLOYED (a first-class, re-registerable legal transition), so the sponsor calls setPoolScope() on the already-funded pool, swaps the account list to one that will be corrupted, re-enters the normal lifecycle, and the corruption sweeps the whole pool away from stakers who only agreed to the original account — no impossible registry rewind involved.
Likelihood:
WHEN a funded pool's sponsor retains the agreement's attack-request role, pauses the pool to suppress the stake/bonus paths that would persist the lock, and requests attack mode — every lock path reachable in ATTACK_REQUESTED (pokeRiskWindow, setPoolScope) reverts and rolls scopeLocked back to false.
WHEN the DAO issues its ordinary soft rejection (rejectAttackRequest: ATTACK_REQUESTED - NOT_DEPLOYED, officially re-registerable), the sponsor replaces the funded pool's scope and re-requests attack mode; the pool relocks around the swapped account.
Impact:
Existing stakers lose the pool's entire principal and bonus to a settlement over an account they never agreed to insure — the pool's core reliance guarantee (exposure bounded by the scope staked against) is broken for a fully-funded pool.
The attack is asymmetric: the sponsor need not stake. A fork run with the sponsor staking nothing and two third parties funding the pool shows the third parties losing 100% (200e18) while the sponsor's capital at risk is zero. Victims cannot self-rescue — the swap happens inside the brief rejected-request window, and once the pool re-seals, withdraw() is permanently disabled.
Differential fork test on the live BattleChain testnet: a control pool and a mutated pool are funded identically; only the mutated pool undergoes the reject-then-replace sequence. Save as test/fork/RejectedRequestScopeRelockBypass.fork.t.sol and run with BATTLECHAIN_TESTNET_RPC=https://testnet.battlechain.com forge test --match-contract RejectedRequestScopeRelockBypassForkTest -vv.
Passing result: the mutated pool pays the whitehat the full 200e18; the control pool returns
133.33 / 66.67 — the two differ only by the scope swap this bug enables.
Freeze scope on the first stake (staker reliance), removing any dependence on a later registry observation. If the post-staging policy must stay, give _observePoolState() a non-reverting way to persist the lock in ATTACK_REQUESTED so a rejected request cannot reopen a funded pool's scope.
Impact - Low Nothing moves when the scope is swapped, and the state afterward is pre-attack, so any staker who notices can withdraw in full. _replaceScope emits ScopeUpdated, which puts the change on-chain where it can be seen. Real loss needs a restarted attack cycle that breaches one of the substituted accounts with the original staker still in the pool, at which point their principal funds a CORRUPTED payout for coverage they never chose. Likelihood - Medium The step the sponsor cannot force is the moderator rejecting the attack request, though rejections are an ordinary registry operation. The rest they can arrange. Neither observation path can seal the lock in this state, and pausing the pool shuts the deposit routes, leaving only a full-exit withdraw to do it. So reaching the mutable-scope condition is not hard; what holds the severity down is how much has to go wrong afterward for anyone to actually lose money.
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.