Last Man Standing

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

Legitimate Winners Can Be Dethroned After Grace Period

Summary

The claimThrone() function allows new throne claims even after the gracePeriod has expired, enabling attackers to steal victories from legitimate winners. Players who have rightfully won the game (by surviving the grace period) can still be dethroned by subsequent claimants, completely breaking the core game mechanics and allowing theft of winnings.

Description

The vulnerability exists because claimThrone() only checks the gameNotEnded modifier but fails to validate whether the gracePeriod has expired for the current king. This creates a critical window between gracePeriod expiration and declaring winner where legitimate winners can be robbed of their victory.

Game Rules (as intended):

  1. Player becomes king by calling claimThrone()

  2. If no one dethrones them within the gracePeriod, they win

  3. Winner gets the entire pot when declareWinner() is called

Current Bug:

  • Even after gracePeriod expires, new players can still call claimThrone()

  • This steals the victory from the player who legitimately survived the grace period

  • The attacker becomes the new king and can win the pot instead

function claimThrone() external payable gameNotEnded nonReentrant {
// @Missing validation: should check if grace period expired
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.");
// @rest of the logic
}

Proof Of Concept

function test_legitimate_winner_can_be_dethroned() public {
// STEP 1: Player1 becomes king legitimately
vm.startPrank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.stopPrank();
assertEq(game.currentKing(), player1);
uint256 winningTimestamp = block.timestamp;
// STEP 2: Grace period expires - Player1 is now the legitimate winner
vm.warp(winningTimestamp + game.gracePeriod() + 1 seconds);
// At this point, player1 has WON the game according to the rules
// No one should be able to dethrone them anymore
// STEP 3: Player2 exploits the vulnerability and steals the victory
vm.startPrank(player2);
uint256 newFee = INITIAL_CLAIM_FEE + (INITIAL_CLAIM_FEE * game.feeIncreasePercentage()) / 100;
// This should REVERT but currently SUCCEEDS
game.claimThrone{value: newFee}();
vm.stopPrank();
// STEP 4: Player2 has stolen the crown from the legitimate winner
assertEq(game.currentKing(), player2, "Legitimate winner was dethroned!");
// STEP 5: Player2 now waits for their grace period and wins
vm.warp(block.timestamp + game.gracePeriod() + 1 seconds);
uint256 potAmount = game.pot();
game.declareWinner();
// STEP 6: Player2 gets the rewards that Player1 rightfully earned
assertEq(game.pendingWinnings(player2), potAmount, "Attacker won illegitimately");
assertEq(game.pendingWinnings(player1), 0, "Legitimate winner got nothing");
// Player1 lost their rightful victory and all associated rewards
}

Impact

  • Theft of Legitimate Winnings: Players who rightfully won by surviving the grace period lose their victory and pot rewards

  • Complete Rule Breakdown: The fundamental "grace period = win condition" rule is completely broken

Mitigation

  • Add grace period validation to prevent claiming throne after grace Period expiration:

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.");
// CRITICAL FIX: Prevent throne claims after grace period expires
+ if (currentKing != address(0)) {
+ require(
+ block.timestamp <= lastClaimTime + gracePeriod,
+ "Game: Grace period has expired. Current king has won."
+ );
+ }
// Rest of logic
}
Updates

Appeal created

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

Game::claimThrone can still be called regardless of the grace period

Support

FAQs

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