In the vault, users deposit assets to receive shares, then call briVault::joinEvent to snapshot their current share balance (balanceOf(msg.sender)) into userSharesToCountry[msg.sender][countryId]. This snapshotted value contributes to totalWinnerShares for proportional payouts after the winner is set.
However, nothing prevents users from making additional deposits after joining but before the event starts. Each deposit mints new shares proportionally, increasing the user's live balanceOf without updating the snapshot. During withdraw, the contract uses the inflated live shares for the withdrawal calculation numerator, while totalWinnerShares (denominator) remains based on the old, smaller snapshots.
This mismatch allows a user to:
Deposit minimally, join to snapshot a tiny share amount.
Deposit a large amount post-join to inflate their live shares.
Withdraw an oversized portion of the vault post-event, over-allocating assets and causing reverts (e.g., insufficient balance) for honest later withdrawers.
Code Highlights:
This breaks fair distribution, as the exploiter's effective weight is amplified post-snapshot, diluting others.
Likelihood: High
Deposits are unrestricted pre-event start; multiple calls per user are allowed.
Economically incentivised: minimal initial risk for large gains.
No checks in deposit for prior joins or snapshot updates.
Impact: High
Exploiters steal funds via inflated claims.
Honest winners face DoS (reverts on withdraw), leading to partial or full fund freezes.
Violates protocol intent of proportional sharing based on bet deposits; could lead to total vault depletion before all winners claim.
Exploit Steps:
User1 (exploiter) deposits minimally (> minimumAmount + fee), joins with countryId 10 (small snapshot).
User1 deposits a large amount post-join (inflates live shares).
User2 and User3 deposit normally, join with countryId 10.
Event starts/ends; the owner sets the winner (10).
User1 and User2 withdraw oversized amounts; User3 reverts due to depleted vault.
Add this test_PostJoinDeposit_InflatesPayouts to briVault.t.sol:
Run the test using the following command:
Logs:
Prevent post-join deposits: Add the following check in deposit
Or update snapshot on each deposit: But this could allow team switches; better to lock after join.
Use snapshotted shares in withdraw (e.g., shares = userSharesToCountry[msg.sender][winnerCountryId];)
Combine deposit and joinEvent into a single function to enforce atomicity.
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.