Last Man Standing

First Flight #45
Beginner FriendlyFoundrySolidity
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Missing Access Control on claimThrone() Allows Anyone to Reset Game State and Hijack King Role

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

In the Game contract, the claimThrone() function is publicly accessible and lacks any form of access control or validation on the caller. Under normal circumstances, this function is expected to be called under specific game rules, likely tied to eligibility conditions such as payment, cooldowns, or role-based permissions.

  • Explain the specific issue or problem in one or more sentences

However, due to the absence of such checks, any external address can arbitrarily call claimThrone() at any time, making themselves the currentKing and resetting the lastClaimTime. This breaks the intended game flow, undermines fairness, and allows malicious actors to hijack the game mechanics without restriction.

The core issue is the missing access or eligibility validation, leading to full game control being exposed to any user on-chain.

function claimThrone() external {
@> currentKing = msg.sender;
@> lastClaimTime = block.timestamp;
}

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

This issue will occur whenever any external user calls the claimThrone() function, as it lacks any form of access control or input validation.

  • Reason 2

It also occurs every time a malicious actor wants to override the currentKing value without restriction, since no checks (e.g., payments, cooldowns, ownership validation) are enforced.

Impact:

  • Impact 1

Attackers can spam the function to continually seize control of the throne, preventing fair gameplay or participation.

  • Impact 2

The game logic becomes unreliable, leading to a broken or exploitable system where the currentKing does not reflect intended behavior or outcomes.

Proof of Concept

Deploy the vulnerable Game contract.

  • Deploy the ExploitKingGame contract, passing the address of the Game contract to the constructor.

  • Call takeOverThrone() from any address — it will instantly make the caller the new currentKing.

  • Repeat the call as many times as desired from any address to override the throne, demonstrating lack of restriction.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IGame {
function claimThrone() external;
function currentKing() external view returns (address);
}
contract ExploitKingGame {
IGame public game;
constructor(address _game) {
game = IGame(_game);
}
function takeOverThrone() external {
game.claimThrone();
}
function checkCurrentKing() external view returns (address) {
return game.currentKing();
}
}

Recommended Mitigation

Modify the claimThrone() function to prevent arbitrary or repeated claims without consequence.

- remove this code
- function claimThrone() public {
+ add this code
+ function claimThrone() public payable {
+ require(block.timestamp >= lastClaimTime + gracePeriod, "Grace period not over");
+ require(msg.value >= 0.01 ether, "Insufficient tribute");
currentKing = msg.sender;
lastClaimTime = block.timestamp;
}
Updates

Appeal created

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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

Give us feedback!