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

Manipulate `MartenitsaMarketplace::collectReward` to mint an unlimited amount of HealthTokens.

Summary

The function collectReward is intended to allow users to mint 1 HealthToken for every 3 different
MartenitsaTokens they held.

Given the fact that the MartenitsaToken::getCountMartenitsaTokensOwner can be manipulated to return any
desired value, and the collectReward function doesn't check that the caller actually
owns 3 distinct MartenitsaTokens, this can be exploited to mint an unlimited amount of HealthTokens.

Vulnerability Details

function collectReward() external {
require(!martenitsaToken.isProducer(msg.sender), "You are producer and not eligible for a reward!");
uint256 count = martenitsaToken.getCountMartenitsaTokensOwner(msg.sender);
// @-audit the line above can be manipulated to return any desired value
uint256 amountRewards = (count / requiredMartenitsaTokens) - _collectedRewards[msg.sender];
// @-audit rewards is not following the spec:
// every 3 **different** MartenitsaTokens they receive 1 HealthToken.
if (amountRewards > 0) {
_collectedRewards[msg.sender] = amountRewards;
healthToken.distributeHealthToken(msg.sender, amountRewards);
}
}

Proof of concept

  1. Attacker calls 3 times the function MartenitsaToken::updateCountMartenitsaTokensOwner() passing its own account as the target address and the operation "add".

  2. The attacker calls collectReward() to receive 1 HealthToken.

  3. Repeat 1 and 2 to obtain the desired amount of HealthTokens.

PoC

Place the following code in MartenitsaMarketplace.t.sol.

function testUnlimitedHealthTokenMints() public {
address attacker = makeAddr("attacker");
for (uint256 i = 0; i < 3; i++) {
vm.prank(attacker);
martenitsaToken.updateCountMartenitsaTokensOwner(address(attacker), "add");
}
vm.prank(attacker);
marketplace.collectReward();
assert(healthToken.balanceOf(address(attacker)) == 10 ** 18);
}

Impact

It's possible to mint an unlimited amount of HealthTokens without actually owning any NFT.

Tools Used

Manual review

Recommended Mitigation

It's required to keep track of the tokenIds of each MartenitsaToken held by each users.
In this way, it will be possible to get the token design and make sure the caller of the
collectReward function actually owns 3 different tokens.

mapping (address => uint256[]) public tokenIdsByUser;
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.