Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: low
Invalid

Incorrect Status Check Leads To Re-registration

Summary

Under a specific condition, a user can call ThePredicter::register() twice.

Vulnerability Details

Intuitively, a user should only register once successfully. However, if the following conditions hold, the user can re-register:

  1. The user registers for the first time by calling ThePredicter::register() and paying the right entrance fee. The user's status is now Status.Pending.

  2. The registration is approved by the organizer through ThePredicter::approvePlayer(). The user's status is now Status.Approved.

After the second step is done, the user can call ThePredicter::register() for the second time because the function only checks whether the user has a status of Status.Pending. Since the user's status is now Status.Approved, the call passes.

Impact

The user loses the second entrance fee. This issue does not seem to be exploitable. However, it causes unrecoverable losses to the user if the event occurs.

Tools Used

Testing, manual review.

Recommendations

Consider changing the code on line 55 with the following snippet:

if (playersStatus[msg.sender] == Status.Pending || playersStatus[msg.sender] == Status.Approved) {

Proof of Concept

The issue can be demonstrated using the following test:

function test_evm_register_approvePlayer_register() public {
address user = users[0];
uint256 entranceFee = thePredicter.entranceFee();
// Register a user for the first time
deal(user, entranceFee);
vm.startPrank(user);
thePredicter.register{value: entranceFee}();
vm.stopPrank();
// Cannot register twice at this point because of Status.Pending
deal(user, entranceFee);
vm.startPrank(user);
vm.expectRevert(ThePredicter__CannotParticipateTwice.selector);
thePredicter.register{value: entranceFee}();
vm.stopPrank();
// Approve the user by the organizer
vm.startPrank(organizer);
thePredicter.approvePlayer(user);
vm.stopPrank();
// Can re-register once the status is no longer Status.Pending
deal(user, entranceFee);
vm.startPrank(user);
thePredicter.register{value: entranceFee}();
vm.stopPrank();
}
Updates

Lead Judging Commences

NightHawK Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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