VotersFor rewards is less than it should be and funds are locked forever due to bad logic implementation in the _distributeRewards() function called internally by vote(). The rewards are calculated as if they were going to be distributed to all the voters, not just the votersFor.
Actors:
Victims: VotersFor when there are votersAgainst
Protocol: the VotingBooth contract
Exploit Scenario:
Initial State: The Protocol is deployed by its creator who sent 1 ETH as minimum funding and gave as argument a list of 5 voters. Hence when the 3rd voter votes, the voting will be considered complete.
Step 1: Victim#1 who is voters[0] in the PoC below votes for the proposal by calling booth.vote(true)
Step 2: voters[2] in the PoC below votes against the proposal by calling booth.vote(false)
Step 3: Victim#2 who is voters[2] in the PoC below votes for the proposal by calling booth.vote(true)
Outcome: victim#1 gets rewarded 0.33...33 ETH instead of 0.5 ETH and victim#2 gets rewarded 0.33...34 ETH instead of 0.5 ETH. The funds that have not been distributed will remain locked inside the protocol.
Implications: Funds will be locked inside the protocol and the voters who voted for the proposol will receive less than they deserve. In the PoC below I assumed the creator sends 1 ether which is the minimum funding, had he send more, the amount of funds locked would increase as well. Voters can predict how much they're owed as nothing is hashed and everything is available onchain and people can read the storage and know who voted for and who voted against the proposal. That would leave them angry at the protocol and the creator.
Manual Analysis and foundry
uint256 rewardPerVoter = totalRewards / totalVotes;
uint256 rewardPerVoter = totalRewards / totalVotesFor;
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.