Last Man Standing

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

Grace Period Manipulation by Owner

Root + Impact

Description

  • The updateGracePeriod() function should allow the owner to adjust grace period settings for future game rounds or during initial contract setup, with reasonable bounds and restrictions that prevent mid-game rule changes. The grace period represents a critical timing parameter that players rely on when making strategic decisions about when to claim the throne, and this value should remain consistent during active gameplay to ensure fair competition and predictable game mechanics.

  • The updateGracePeriod() function can be called by the owner at any time without restrictions, allowing real-time manipulation of game timing rules during active gameplay. The function only validates that the new period is greater than zero but lacks reasonable upper and lower bounds, permitting extreme values from 1 second to effectively infinite timeframes. This enables the owner to dynamically adjust grace periods based on their current position in the game: extending periods when they are king to prevent being dethroned, or shortening periods when opponents are king to force premature game endings, fundamentally breaking the fairness and predictability that competitive games require.

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
@> require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
@> gracePeriod = _newGracePeriod; // NO RESTRICTIONS ON TIMING OR BOUNDS
emit GracePeriodUpdated(_newGracePeriod);
}

The function lacks restrictions preventing changes during active games and has no reasonable bounds checking for extreme values.


Risk

Likelihood:

  • This vulnerability activates whenever the owner's position in the current game becomes threatened or advantageous, as they can immediately call updateGracePeriod() to modify timing rules in real-time based on whether they currently hold the king position or want to accelerate game endings

  • The manipulation occurs during any active game session where the owner decides to optimize their winning chances, as the function imposes no restrictions on timing changes during ongoing gameplay and allows both extremely short periods (forcing quick endings) and extremely long periods (preventing game completion indefinitely)

Impact:

  • Unfair competitive advantage for the owner who can extend grace periods when they are king to maintain their position indefinitely, or shorten grace periods when opponents are king to force premature game endings before other players can respond

  • Destruction of game integrity and player trust through arbitrary rule changes that violate player expectations, as participants enter games with specific timing assumptions that can be unilaterally altered mid-game without consent, effectively creating a rigged game environment where only the owner has control over victory conditions

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test, console2} from "forge-std/Test.sol";
import {Game} from "../src/Game.sol";
/**
* @title Grace Period Manipulation PoC
* @notice Demonstrates how owner can manipulate grace period mid-game to influence outcomes
*/
contract GracePeriodManipulationPoC is Test {
Game public game;
address public owner; // Test contract is owner
address public player1;
address public player2;
uint256 public constant INITIAL_CLAIM_FEE = 0.1 ether;
uint256 public constant INITIAL_GRACE_PERIOD = 1 days;
uint256 public constant FEE_INCREASE_PERCENTAGE = 10;
uint256 public constant PLATFORM_FEE_PERCENTAGE = 5;
function setUp() public {
owner = address(this); // Test contract is the owner
player1 = makeAddr("player1");
player2 = makeAddr("player2");
vm.deal(owner, 10 ether);
vm.deal(player1, 10 ether);
vm.deal(player2, 10 ether);
game = new Game(
INITIAL_CLAIM_FEE,
INITIAL_GRACE_PERIOD,
FEE_INCREASE_PERCENTAGE,
PLATFORM_FEE_PERCENTAGE
);
}
/**
* @notice Demonstrates owner can manipulate grace period during gameplay
*/
function test_OwnerCanChangeGracePeriodAnytime() public {
uint256 originalPeriod = game.gracePeriod();
console2.log("Original grace period:", originalPeriod);
// Owner can change grace period at any time
uint256 newPeriod = 7 days;
game.updateGracePeriod(newPeriod);
assertEq(game.gracePeriod(), newPeriod);
console2.log("Owner changed grace period to:", newPeriod);
// Can change again immediately
uint256 veryShortPeriod = 1 minutes;
game.updateGracePeriod(veryShortPeriod);
assertEq(game.gracePeriod(), veryShortPeriod);
console2.log("Owner changed grace period again to:", veryShortPeriod);
}
/**
* @notice Shows owner can set extreme values
*/
function test_OwnerCanSetExtremeValues() public {
console2.log("Testing extreme grace period values...");
// Very short period (1 second)
game.updateGracePeriod(1);
assertEq(game.gracePeriod(), 1);
console2.log("Set to 1 second");
// Very long period (100 years)
uint256 centuryInSeconds = 100 * 365 * 24 * 60 * 60;
game.updateGracePeriod(centuryInSeconds);
assertEq(game.gracePeriod(), centuryInSeconds);
console2.log("Set to 100 years");
console2.log("No reasonable bounds checking exists");
}
/**
* @notice Demonstrates impact on game fairness and player expectations
*/
function test_GameFairnessViolation() public {
uint256 initialPeriod = game.gracePeriod();
console2.log("Players expect consistent grace period:", initialPeriod);
// Owner changes rules mid-game without notice
uint256 manipulatedPeriod = initialPeriod * 5; // 5x longer
game.updateGracePeriod(manipulatedPeriod);
console2.log("Owner changed grace period to:", manipulatedPeriod);
console2.log("Player strategies invalidated by rule change");
assertNotEq(game.gracePeriod(), initialPeriod);
}
/**
* @notice Shows owner can create unwinnable scenarios
*/
function test_CreateUnwinnableScenario() public {
// Set impossibly long grace period
uint256 impossiblePeriod = 100 * 365 days; // 100 years
game.updateGracePeriod(impossiblePeriod);
console2.log("Owner set grace period to 100 years");
console2.log("Game becomes effectively unwinnable");
assertEq(game.gracePeriod(), impossiblePeriod);
console2.log("Players' potential winnings locked for unreasonable timeframe");
}
/**
* @notice Demonstrates lack of restrictions on manipulation
*/
function test_NoRestrictionsOnManipulation() public {
console2.log("=== NO RESTRICTIONS ON MANIPULATION ===");
console2.log("Current validation only checks > 0");
console2.log("Missing: restrictions during active games");
console2.log("Missing: reasonable bounds");
// Owner can change to any extreme value at any time
uint256[] memory testPeriods = new uint256[](4);
testPeriods[0] = 1; // 1 second
testPeriods[1] = 1 minutes;
testPeriods[2] = 1 days;
testPeriods[3] = 365 days;
for (uint i = 0; i < testPeriods.length; i++) {
game.updateGracePeriod(testPeriods[i]);
assertEq(game.gracePeriod(), testPeriods[i]);
console2.log("Successfully set grace period to:", testPeriods[i]);
}
console2.log("All extreme values pass validation");
console2.log("No protection against manipulation");
}
}

This PoC proves the vulnerability by demonstrating multiple manipulation scenarios where the owner changes grace periods at will during gameplay. The tests show the owner can set extreme values from 1 second to 100 years, change periods multiple times in succession, and create unwinnable scenarios by setting impossibly long grace periods. The PoC confirms there are no restrictions preventing real-time rule changes, no reasonable bounds checking, and no player protection mechanisms against arbitrary manipulation.


Recommended Mitigation

Add restrictions to prevent grace period changes during active games (when a king exists and the game hasn't ended) and implement reasonable bounds (1 hour to 7 days) to prevent extreme manipulation.

function updateGracePeriod(uint256 _newGracePeriod) external onlyOwner {
require(_newGracePeriod > 0, "Game: New grace period must be greater than zero.");
+ require(gameEnded || currentKing == address(0), "Game: Cannot change grace period during active game.");
+ require(_newGracePeriod >= 1 hours && _newGracePeriod <= 7 days, "Game: Grace period must be between 1 hour and 7 days.");
gracePeriod = _newGracePeriod;
emit GracePeriodUpdated(_newGracePeriod);
}
Updates

Appeal created

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!