Eggstravaganza

First Flight #37
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

Potential endTime Overflow in EggHuntGame

Description

The EggHuntGame contract suffers from a potential integer overflow when setting the endTime variable. Specifically, when calculating the endTime by adding a specified game duration to block.timestamp, a substantial duration could cause an overflow.

The endTime is set as follows:

endTime = block.timestamp + duration;

If duration is large enough, adding it to block.timestamp could cause the resulting endTime to exceed the maximum value allowed for a uint256 (i.e., type(uint256).max), causing an overflow.

This vulnerability has two potential attack vectors:

  1. Denial of Service (DoS) Attack: By providing an extremely large duration, an attacker could prevent the game from starting due to the overflow condition, effectively locking the game contract in a state where no game can be initiated.

  2. Unexpected Game End Conditions: If this overflow occurs, it might result in an incorrect or impossibly large endTime, making it difficult or impossible to end the game.

Proof of Concept (PoC)

Below is a PoC demonstrating the overflow vulnerability when setting the endTime with a dangerously large duration:

function testEndTimeOverflowVulnerability() public {
// Get the max uint256 value
uint256 maxUint = type(uint256).max;
// Calculate a duration that would cause overflow when added to block.timestamp
uint256 dangerousDuration = maxUint - block.timestamp + 1;
// Ensure the duration is above the minimum required
assertGe(dangerousDuration, game.MIN_GAME_DURATION(), "Duration should be above minimum");
// Expect a revert with panic code 0x11 (arithmetic overflow)
vm.expectRevert(abi.encodeWithSignature("Panic(uint256)", 0x11));
// Start game with the dangerous duration - this should revert
game.startGame(dangerousDuration);
// The test will pass if the transaction reverts with the expected panic code
}

Explanation of PoC:

  1. Maximum uint256 Value: We use type(uint256).max to obtain the largest possible uint256 value.

  2. Dangerous Duration Calculation: By calculating the dangerousDuration as maxUint - block.timestamp + 1, we ensure that adding this duration to block.timestamp will cause an overflow, as the result exceeds the uint256 max value.

  3. Expecting a Revert: The test expects the transaction to revert due to the arithmetic overflow (Panic(uint256) 0x11).

Impact

This vulnerability can lead to several issues:

1. Denial of Service (DoS) Attack

  • An attacker can exploit this vulnerability by providing an extremely large duration, causing the endTime calculation to overflow. This prevents the game from starting, as the contract will revert with an overflow error.

  • This can effectively lock out the game functionality, preventing any new game sessions from being initiated.

2. Incorrect or Impossible endTime Values

  • If the overflow occurs without immediate detection (e.g., through insufficient input validation), the game might end prematurely or the endTime could become an enormous future value, leading to unpredictable game behavior.

  • This can confuse players and impact game dynamics, as they might not be able to predict when the game will end or whether the game is active.

3. Overall Stability and Reliability

  • The game's core functionality relies on accurate time tracking for game sessions. Overflow vulnerabilities like this one undermine the contract's reliability and stability, particularly for players who rely on fair game durations and session timelines.

Recommendations

To prevent the endTime overflow, we recommend the following fixes:

Option 1: Validate Duration Before Calculating endTime

Before setting the endTime, ensure that adding the duration to block.timestamp does not cause an overflow. This can be done by checking if the duration is small enough to avoid exceeding the maximum uint256 value.

function startGame(uint256 duration) external onlyOwner {
require(!gameActive, "Game already active");
require(duration >= MIN_GAME_DURATION, "Duration too short");
// Prevent overflow by ensuring duration won't cause `endTime` to exceed uint256 max
require(block.timestamp + duration >= block.timestamp, "Duration too large, overflow risk");
startTime = block.timestamp;
endTime = block.timestamp + duration;
gameActive = true;
emit GameStarted(startTime, endTime);
}

Option 2: Use a More Restrictive Maximum Duration

Instead of allowing any arbitrary duration, limit it to a maximum acceptable value. This would prevent durations that are so large they could cause overflows while still allowing flexibility for typical game durations.

uint256 public constant MAX_GAME_DURATION = 365 days; // Example max duration
function startGame(uint256 duration) external onlyOwner {
require(!gameActive, "Game already active");
require(duration >= MIN_GAME_DURATION, "Duration too short");
require(duration <= MAX_GAME_DURATION, "Duration too long");
startTime = block.timestamp;
endTime = block.timestamp + duration;
gameActive = true;
emit GameStarted(startTime, endTime);
}

Conclusion

The potential overflow in the endTime calculation can disrupt the normal operation of the game, either by preventing it from starting or by creating erroneous game end conditions. By validating durations before adding them to block.timestamp or introducing a maximum allowable duration, this vulnerability can be mitigated, ensuring a more secure and predictable game experience.

Updates

Lead Judging Commences

m3dython Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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