Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

Incorrect Caller Checks in `claimThrone`

Incorrect checks in claimThrone function, making no one can enters

Description

The require(msg.sender == currentKing,) checks whether the caller is the current king, but this checks is actually checking that caller must be the current king. At the initial contract deployment, currentKing is not set anywhere, therefore this function only allows address(0) caller.

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
// @audit-high
@> require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}

Risk

Likelihood:

  • Hight, because the currentKing is the address(0), and this function actually only allows address(0) to be the caller

Impact:

  • Since this function only allows address(0) to play, the real user with non-zero address holder cannot enters

Proof of Concept

function test_only_allow_zeroAddress(address _user) public {
vm.deal(_user, INITIAL_CLAIM_FEE);
vm.startPrank(_user);
if (_user == address(0)) {
// Expecting Zero Address Caller will Reverts
// But it doesn't
game.claimThrone{value: INITIAL_CLAIM_FEE}();
return;
}
// when real user trynna enters
// no one can do it because it only allows zero address caller :(
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
}
}

And surprisingly this test passes:

$ forge test --mt test_only_allow_zeroAddress
[⠊] Compiling...
[⠃] Compiling 2 files with Solc 0.8.30
[⠊] Solc 0.8.30 finished in 830.45ms
Compiler run successful!
Ran 1 test for test/Game.t.sol:GameTest
[PASS] test_only_allow_zeroAddress(address) (runs: 256, μ: 46466, ~: 46144)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 28.19ms (16.35ms CPU time)
Ran 1 test suite in 35.22ms (28.19ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

/**
* @dev Allows a player to claim the throne by sending the required claim fee.
* If there's a previous king, a small portion of the new claim fee is sent to them.
* A portion also goes to the platform owner, and the rest adds to the pot.
*/
function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
// @audit-high
- require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
+ require(msg.sender != currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
uint256 previousKingPayout = 0;
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
Updates

Appeal created

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Game::claimThrone `msg.sender == currentKing` check is busted

thesandf Auditor
about 2 months ago
inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Game::claimThrone `msg.sender == currentKing` check is busted

Support

FAQs

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