Last Man Standing

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

Owner Can Rob Winner


Description

The updatePlatformFeePercentage function allows the contract owner to update the platformFeePercentage, which determines the portion of each throne claim’s ETH that goes to the platform fees instead of the game’s pot. The new percentage must be valid (0-100), as enforced by the isValidPercentage modifier.

The function has a critical vulnerability: the owner can change platformFeePercentage during an active game, potentially setting it to 100% to divert all subsequent claim amounts to platform fees, robbing the winner of the expected pot growth. This manipulation can occur at any time, undermining the game’s fairness and reducing or eliminating the winner’s payout. Note that the claimThrone function currently has a bug (require(msg.sender == currentKing, ...)), which prevents any claims and will be corrected to require(msg.sender != currentKing, ...) to make the game functional, making this vulnerability exploitable once fixed.

function updatePlatformFeePercentage(uint256 _newPlatformFeePercentage)
external
onlyOwner
isValidPercentage(_newPlatformFeePercentage)
{
//@> platformFeePercentage = _newPlatformFeePercentage;
//@> emit PlatformFeePercentageUpdated(_newPlatformFeePercentage);
}

Risk

Likelihood:

  • The owner can call updatePlatformFeePercentage at any point during an active game, as there are no restrictions on when the function can be invoked.

  • Once the claimThrone bug (require(msg.sender == currentKing, ...)) is corrected to require(msg.sender != currentKing, ...) as planned, players can claim the throne, making the platform fee manipulation exploitable immediately after any claim.

Impact:

  • The winner’s pot is significantly reduced or entirely eliminated if the owner sets platformFeePercentage to 100%, as all subsequent claim amounts go to platform fees instead of the pot.

  • Players lose trust in the game’s fairness, as their contributions to the pot can be diverted to the owner, discouraging participation and potentially causing financial loss.

Proof of Concept

The following unit test demonstrates the critical vulnerability in the updatePlatformFeePercentage function, where the owner can increase the platform fee to 100% during an active game, diverting all subsequent claim amounts to platform fees and preventing the game’s pot from growing, thus robbing the winner of expected winnings. The test assumes the claimThrone bug is fixed (require(msg.sender != currentKing, ...)), allowing players to claim the throne and making the platform fee manipulation exploitable.

// PoC for Owner can increase platform fee to rob winner
function testOwnerIncreasePlatformFeeToRobWinner() public {
// Player1 claims the throne
vm.prank(player1);
game.claimThrone{value: INITIAL_CLAIM_FEE}();
uint256 initialPot = game.pot(); // 0.095 ETH (0.1 ETH - 5% platform fee)
// Owner sets platformFeePercentage to 100%
vm.prank(deployer);
game.updatePlatformFeePercentage(100);
// Player2 claims, all ETH goes to platform fees
vm.prank(player2);
game.claimThrone{value: game.claimFee()}();
assertEq(game.pot(), initialPot, "Pot should not increase with 100% platform fee");
// End game, player2 wins
vm.warp(block.timestamp + GRACE_PERIOD + 1);
vm.prank(player3);
game.declareWinner();
assertEq(game.pendingWinnings(player2), initialPot, "Player2 should only get initial pot");
// Owner withdraws platform fees
uint256 ownerBalanceBefore = deployer.balance;
uint256 platformFees = game.platformFeesBalance();
vm.prank(deployer);
game.withdrawPlatformFees();
assertEq(deployer.balance, ownerBalanceBefore + platformFees, "Owner did not receive platform fees");
}

Recommended Mitigation

Restrict changes to platformFeePercentage to only occur when the game has ended, ensuring that the fee percentage remains fixed during an active game round. This prevents the owner from manipulating the fee mid-game to divert funds from the pot. Additionally, consider adding a cap to the maximum platform fee percentage to prevent extreme values (e.g., 100%).

+ modifier gameEndedOnly() {
+ require(gameEnded, "Game: Can only perform this action when game has ended.");
+ _;
+ }
+ uint256 public constant MAX_PLATFORM_FEE_PERCENTAGE = 20; // Example cap
function updatePlatformFeePercentage(uint256 _newPlatformFeePercentage)
external
onlyOwner
isValidPercentage(_newPlatformFeePercentage)
+ gameEndedOnly
{
+ require(_newPlatformFeePercentage <= MAX_PLATFORM_FEE_PERCENTAGE, "Game: Platform fee percentage too high.");
platformFeePercentage = _newPlatformFeePercentage;
emit PlatformFeePercentageUpdated(_newPlatformFeePercentage);
}

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!