Last Man Standing

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

Inverted King Check Blocks Game::claimThrone() Functionality

Description

The Game::claimThrone()` function contains a critical inverted condition that prevents new players from claiming the throne while incorrectly allowing the current king to reclaim it. This flaw fundamentally breaks the core game mechanic where players compete to become king by paying an increasing fee.

// Root cause in the codebase with @> marks to highlight the relevant section
/**
* @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.");
@> 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
);
}

The condition require(msg.sender == currentKing, ...) is logically inverted. Instead of preventing the current king from reclaiming (as intended), it only permits the current king to call the function. This violates the game's design where:

  • New players should replace the current king by paying the claim fee

  • The current king should be blocked from reclaiming

  • The first claim should set the initial king

Risk

Likelihood:

  • Probability: Certain (100%)

  • Attack Vector: Passive (Triggers on normal usage)

  • Prerequisites: None

Every valid game interaction attempt will trigger this flaw. The game cannot start or progress without patching.

Impact:

  • Renders contract non-functional

  • Permanent game deadlock

Proof of Concept

// PoC Test Case (Foundry)
function test_ClaimThroneFailure() public {
// Attempt first claim with 1 ETH (should succeed but fails)
vm.deal(player1, 1 ether);
vm.prank(player1);
vm.expectRevert("Game: You are already the king. No need to re-claim.");
game.claimThrone{value: 1 ether}(); // Reverts
// Force-set king (hypothetical bypass)
game.setKing(player1); // Not actual function - for demonstration only
// Current king attempts reclaim (should fail but succeeds)
vm.prank(player1);
game.claimThrone{value: 1 ether}(); // Succeeds (invalid)
}

Observed Behavior:

  • First claim attempt always reverts

  • Only existing king can successfully call the function

  • Contract enters permanent locked state after deployment

Recommended Mitigation

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Insufficient ETH");
- require(msg.sender == currentKing, "You are already king");
+ require(msg.sender != currentKing, "Current king cannot reclaim");
// ... rest of logic ...
}

Action Required:

  • Apply code patch immediately

  • Conduct full regression test suite

  • Deploy new contract instance

  • Submit patched code for re-audit

Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

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

nhippolyt Submitter
about 1 month ago
inallhonesty Lead Judge
about 1 month ago
inallhonesty Lead Judge about 1 month 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.