Last Man Standing

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

Owner can manipulate game outcome through dynamic grace period changes

Description:

The updateGracePeriod() function allows the owner to modify the grace period at any time during an active game, without any restrictions or timelock mechanisms. This creates a critical governance vulnerability where the owner can manipulate game outcomes to their advantage.

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
gracePeriod = _newGracePeriod;
emit GracePeriodUpdated(_newGracePeriod);
}

The function lacks any checks for:

  • Whether the game is currently active

  • Whether the current grace period has already expired

  • Any minimum/maximum bounds on the new grace period

  • Any timelock or delay mechanisms

Attack path:

Scenario 1 - Delaying winner declaration:

  1. Game is active with King A, grace period is 24 hours

  2. 23 hours pass, King A is about to win

  3. Owner calls updateGracePeriod(86400 * 365) (1 year)

  4. King A's victory is indefinitely delayed

Scenario 2 - Forcing immediate winner:

  1. Game is active with multiple players competing

  2. Owner's preferred player becomes king

  3. Owner calls updateGracePeriod(1) (1 second)

  4. Owner immediately calls declareWinner() to fix their preferred winner

Scenario 3 - Post-expiry manipulation:

  1. Grace period expires, King A should win

  2. Before anyone calls declareWinner(), owner increases grace period

  3. More players can claim throne, changing the rightful winner

Impact:

Owner can arbitrarily decide game winners regardless of legitimate gameplay

PoC:

Change this line in claimThore() for sucessful test run

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

Put this into Game.t.sol file and run forge test --mt testOwnerCanChangeGracePeriodWhenGameIsActive -vvv

function testOwnerCanChangeGracePeriodWhenGameIsActive() public {
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
vm.warp(block.timestamp + GRACE_PERIOD + 1 hours);
console2.log("Grace period is over. Player 1 should be the winner");
vm.prank(deployer);
game.updateGracePeriod(2 days);
vm.expectRevert();
game.declareWinner();
}

Recommended Mitigation:

  1. Restrict updates during active games:

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner gameEndedOnly {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
initialGracePeriod = _newGracePeriod; // Only update for next round
emit GracePeriodUpdated(_newGracePeriod);
}
Updates

Appeal created

inallhonesty Lead Judge 29 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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