withdraw() validates winners by comparing keccak256(abi.encodePacked(userToCountry[msg.sender])) with the stored winner string (src/briVault.sol:257, src/briVault.sol:299-303). If the owner configures two slots in teams with identical (or empty) names, bettors on the losing index still pass the string check. _getWinnerShares only credits the actual winning index, so a losing participant can call withdraw() first and receive the entire prize pool while legitimate winners subsequently revert.
Likelihood: Medium – it relies on a misconfigured tournament (duplicate or unset team names), but such operator mistakes are common and easy for attackers to notice.
A losing bettor can withdraw the entire vault balance if their team name matches the declared winner.
Honest winners revert on withdraw() after the vault is emptied.
Owner sets teams[0] = "Alpha" and teams[1] = "Alpha".
User A joins team 0; User B joins team 1.
After the event, owner calls setWinner(0).
User B (loser) calls withdraw() first and receives the full pot.
User A (winner) calls withdraw() and reverts because the vault balance is zero.
(Reproduced in test/BriVaultAttack.t.sol:228 via testDuplicateTeamNameLetsLoserStealPool.)
Reject duplicate or empty team identifiers when calling setCountry (e.g., track seen hashes or enforce bytes(countries[i]).length > 0 and uniqueness).
Store and compare canonical country IDs (indices) instead of strings when validating winners and participants.
Add regression tests covering misconfigured tournaments to ensure losers cannot satisfy the winner check.
Proposed patch (Solidity-like pseudocode):
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.