Last Man Standing

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

Mutable Grace Period Vulnerability

Root + Impact

Description

  • Normally, the grace period should remain constant during an active game round to ensure fair gameplay and predictable reward timing.

  • The contract allows the owner to modify gracePeriod mid-game via updateGracePeriod(), enabling potential manipulation of game outcomes.

@> function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
@> gracePeriod = _newGracePeriod;
@> }
// No game state checks - can be changed while round is active

Risk

Likelihood: Medium

  • Requires owner privilege but no technical barriers

  • Could be exploited:

    1. To prematurely end games when favorable to owner

    2. To artificially extend games when owner is current king

Impact: High

  • Fairness Violation: Undermines game's trustless nature

  • Economic Manipulation: Allows owner to influence prize distribution

  • Reputation Damage: Erodes player trust in contract immutability

Proof of Concept

function testOwnerCanManipulateGracePeriod() public {
// 1. Player claims throne
vm.prank(player1);
game.claimThrone{value: 1 ether}();
// 2. Owner shortens grace period right before expiration
vm.warp(block.timestamp + 3599 seconds); // 1 second left
vm.prank(owner);
game.updateGracePeriod(1); // Set to 1 second
// 3. Owner immediately declares themselves winner
vm.warp(block.timestamp + 2 seconds);
game.declareWinner();
// Player loses despite originally having 24h protection
assertEq(game.currentKing(), player1); // But winner is declared
}

Key Exploit Steps:

  1. Player legitimately claims throne (expects full grace period)

  2. Owner waits until near period end

  3. Owner reduces grace period to minimal duration

  4. Owner immediately declares victory before players can react


Recommended Mitigation

Option 1: Lock During Active Game (Recommended)

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
+ require(gameEnded, "Cannot change during active game");
gracePeriod = _newGracePeriod;
}
  • Locks grace period during active games (!gameEnded)

  • Only allows changes between rounds

  • Maintains all other safety checks

Option 2: Emit Warning Event

event GracePeriodChanged(uint256 oldPeriod, uint256 newPeriod, bool gameActive);
function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
emit GracePeriodChanged(gracePeriod, _newGracePeriod, !gameEnded);
gracePeriod = _newGracePeriod;
}
Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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