Last Man Standing

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

all user funds are taken as platform fees, and the pot does not increase.

If platformFeePercentage is 100%, all user funds are taken as platform fees, and the pot does not increase.

Description

if the platformFeePercentage is a 100% all user funds are taken as platform fees and bypassing "Defensive check" because now currentPlatformFee == (sentAmount - previousKingPayout) and after that pot balance wil be 0 because its calculated from sentAmount - currentPlatformFee, but the vulnerability is not just that, the previousKingPayout variable doesn't initialize anywhere even after the previous king really payout because the variable it's only initialized inside the function, so since the previous king payout is initialized as 0 which means the "Defensive checks" is not gonna work because all user funds are taken as platform fees, and the pot does not increase.

function claimThrone() external payable gameNotEnded nonReentrant {
require(msg.value >= claimFee, "Game: Insufficient ETH sent to claim the throne.");
//@audit-issue how user will be able to enter the game if the msg.sender need to be current king?
require(msg.sender == currentKing, "Game: You are already the king. No need to re-claim.");
uint256 sentAmount = msg.value;
@> uint256 previousKingPayout = 0; // always be 0
uint256 currentPlatformFee = 0;
uint256 amountToPot = 0;
// Calculate platform fee
currentPlatformFee = (sentAmount * platformFeePercentage) / 100;
// Defensive check to ensure platformFee doesn't exceed available amount after previousKingPayout
//@audit-issue `previousKingPayout` always zero ⇒ check always passes ⇒ fee unchecked
//pot becomes 0 if platformFeePercentage == 100
@> if (currentPlatformFee > (sentAmount - previousKingPayout)) {
currentPlatformFee = sentAmount - previousKingPayout;
}
platformFeesBalance = platformFeesBalance + currentPlatformFee;
// Remaining amount goes to the pot
amountToPot = sentAmount - currentPlatformFee;
pot = pot + amountToPot;
// Update game state
currentKing = msg.sender;
lastClaimTime = block.timestamp;
playerClaimCount[msg.sender] = playerClaimCount[msg.sender] + 1;
totalClaims = totalClaims + 1;
// Increase the claim fee for the next player
claimFee = claimFee + (claimFee * feeIncreasePercentage) / 100;
emit ThroneClaimed(
msg.sender,
sentAmount,
claimFee,
pot,
block.timestamp
);
}

Risk

Likelihood:

  • This vulnerability will occur when the platformFeePercentage is 100% or,

  • If the currentPlatformFee > (sentAmount - previousKingPayout) after the first calculation.

Impact:

  • All user funds are taken as platform fess

  • pot does not increase and the winner win nothing

Proof of Concept

here's the test:

function test_PotBecome0IfPlatformFeePercentageIs100() public {
// checks platform fee percentage before update
uint256 platformFeePercentageBeforeUpdate = game.platformFeePercentage();
assertEq(platformFeePercentageBeforeUpdate, PLATFORM_FEE_PERCENTAGE);
console2.log("\n platform Fee Percentage Before Update: %s", platformFeePercentageBeforeUpdate,"%");
// 1. Owner set platform fee percentage to a 100
vm.startPrank(deployer);
game.updatePlatformFeePercentage(100);
vm.stopPrank();
// checks platform fee percentage before update
uint256 platformFeePercentageAfterUpdate = game.platformFeePercentage();
assertEq(platformFeePercentageAfterUpdate, 100);
console2.log("\n platform Fee Percentage After Update: %s", platformFeePercentageAfterUpdate,"%");
// checks platform fees balance and pot balance before user claim throne
uint256 platformFeesBalanceBefore = game.platformFeesBalance();
uint256 potBalanceBefore = game.pot();
assertEq(platformFeesBalanceBefore, 0);
assertEq(potBalanceBefore, 0);
console2.log("\n platform fees balance before user claim throne: %s", platformFeesBalanceBefore);
console2.log("\n pot balance before user claim throne: %s", potBalanceBefore);
// 2. Player1 claim throne by sending required claim fee or more
vm.startPrank(player1);
game.claimThrone{value: 1 ether}(); // sent 1 ether for simplicity
vm.stopPrank();
// checks platform fees balance and pot balance after user claim throne
uint256 platformFeesBalanceAfter = game.platformFeesBalance();
uint256 potBalanceAfter = game.pot();
assertEq(platformFeesBalanceAfter, 1 ether);
assertEq(potBalanceAfter, 0);
console2.log("\n platform fees balance after user claim throne: %s", platformFeesBalanceAfter);
console2.log("\n pot balance after user claim throne: %s", potBalanceAfter);
}

and the log from the test:

Ran 1 test for test/Game.t.sol:GameTest
[PASS] test_PotBecome0IfPlatformFeePercentageIs100() (gas: 160838)
Logs:
platform Fee Percentage Before Update: 5 %
platform Fee Percentage After Update: 100 %
platform fees balance before user claim throne: 0
pot balance before user claim throne: 0
platform fees balance after user claim throne: 1000000000000000000
pot balance after user claim throne: 0

Recommended Mitigation

  • Hard Limit on Platform Fee
    Explicitly limit the platformFeePercentage value, Place in constructor, setter, or modifier:

+ require(platformFeePercentage <= 10, "Platform fee too high");
  • Delete Fake Checks if They Are Useless
    If previousKingPayout is indeed not used, delete all these fake checks:

- if (currentPlatformFee > (sentAmount - previousKingPayout)) {
- currentPlatformFee = sentAmount - previousKingPayout;
- }
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.