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

No restriction implemented in `MartenitsaToken::updateCountMartenitsaTokensOwner` allows any user to update any MartenitsaToken balance breaking the operativity and purpose of the protocol

No restriction implemented in MartenitsaToken::updateCountMartenitsaTokensOwner allows you to update any token balance breaking the operativity and purpose of the protocol

Summary

If an user wants to buy a MartenitsaToken, it's supposed to call MartenitsaMarketplace::buyMartenitsa to purchase it, where there are the necessary checks to verify that the user has the requirements to do so. The balance of both the buyer and seller is updated by calling the function updateCountMartenitsaTokensOwner from the contract MartenitsaToken.

However, an user can directly call MartenitsaToken::updateCountMartenitsaTokensOwner, bypassing any previous restriction, to update its own balance or that of any other user as there is no control over who is calling the function. This means that an attacker can negatively or positively influence not only its own balance, but also that of other users.

Vulnerability Details

If you look at MartenitsaToken::updateCountMartenitsaTokensOwner, you can see that the there is no restriction implemented for the function. This means that any user can call this function acting on the balance of every other user partecipating in the protocol.

Code
@> function updateCountMartenitsaTokensOwner(address owner, string memory operation) external {
@> 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");
}
}

You can test this by adding testUnrestricted_updateCountMartenitsaTokensOwner() to MartenitsaToken.t.sol test suite. A possible solution, is to make

Proof of Code
function testUnrestricted_updateCountMartenitsaTokensOwner() public createMartenitsa {
address newUser = makeAddr("newUser");
address evilUser = makeAddr("evilUser");
vm.startPrank(newUser);
for (uint256 i = 0; i < 100; i++) {
martenitsaToken.updateCountMartenitsaTokensOwner(newUser, "add");
}
vm.stopPrank();
assert(martenitsaToken.getCountMartenitsaTokensOwner(newUser) == 100);
vm.startPrank(evilUser);
for (uint256 i = 0; i < 100; i++) {
martenitsaToken.updateCountMartenitsaTokensOwner(newUser, "sub");
}
vm.stopPrank();
assert(martenitsaToken.getCountMartenitsaTokensOwner(newUser) == 0);
}

Impact

This enables anyone to reduce or increase any balance of MartenitsaToken of any user, breaking the purpose of MartenitsaMarketplace::buyMartenitsa and the whole purpose of the protocol in general.

Tools Used

Manual Review, Foundry

Recommendations

You should implement some checks on the function MartenitsaToken::updateCountMartenitsaTokensOwner to see who is calling it. One possible solution is the following.

Code
+import {MartenitsaMarketplace} from "./MartenitsaMarketplace.sol";
...
+ MartenitsaMarketplace private _martenitsaMarketplace;
...
+ function setMarketAddress(address martenitsaMarketplace) public onlyOwner {
+ _martenitsaMarketplace = MartenitsaMarketplace(martenitsaMarketplace);
+ }
...
function updateCountMartenitsaTokensOwner(address owner, string memory operation) external {
+ require(msg.sender == address(_martenitsaMarketplace), "Unable to call this function");
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.