Last Man Standing

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

Denial of Service in claimThrone Due to Incorrect currentKing Check

Description

The Game contract enables players to claim the throne by paying an increasing claimFee, becoming the currentKing, with the last king winning the pot after the gracePeriod expires. The claimThrone function require (msg.sender == currentKing, ...) check incorrectly reverts for all callers, as players cannot be the currentKing when claiming, causing a Denial of Service (DoS) that prevents throne claims.

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.");

Risk

Likelihood:

Every claimThrone call triggers the check, which fails since msg.sender cannot equal currentKing for new claimants.
Initial currentKing is address(0), ensuring the check fails for the first claimant and all subsequent attempts.

Impact:

Players cannot claim the throne, rendering the game unplayable and violating the player/king actor roles.
Funds sent with failed transactions are reverted, halting game progression and preventing pot accumulation.

Proof of Concept

function test_ClaimThrone_alwaysrevert() public {
vm.startPrank(player1);
uint256 initialBalance = player1.balance;
address initialKing = game.currentKing();
// Expect the claimThrone call to revert
vm.expectRevert(bytes("Game: You are already the king. No need to re-claim."));
game.claimThrone{value: INITIAL_CLAIM_FEE}();
// Verify no state changes occurred due to revert
assertEq(game.currentKing(), initialKing, "Current king should not change");
assertEq(player1.balance, initialBalance, "Player balance should not change");
vm.stopPrank();
}

Tool Used

Manual Review

Code Snippet

https://github.com/CodeHawks-Contests/2025-07-last-man-standing/blob/47d9d19a78acb52270269f4bff1568b87eb81a96/src/Game.sol#L186

Recommended Mitigation

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.");
+ require(msg.sender != currentKing, "Game: You are already the king. Cannot re-claim.");
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

Support

FAQs

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