Platform: Code4rena
Start Date: 11/08/2022
Pot Size: $40,000 USDC
Total HM: 8
Participants: 108
Period: 4 days
Judge: hickuphh3
Total Solo HM: 2
Id: 152
League: ETH
Rank: 21/108
Findings: 3
Award: $104.69
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: itsmeSTYJ
Also found by: 0x1f8b, 0x52, 0xDjango, Ch_301, Chom, KIntern_NA, PwnedNoMore, Treasure-Seeker, auditor0517, byndooa, cccz, csanuragjain, ladboy233, nine9, shenwilly, thank_you, yixxas, zkhorse
42.8343 USDC - $42.83
Detailed description of the impact of this finding.
if (IERC721(nftContract).balanceOf(msg.sender) + count > saleConfig.limitPerAccount) { if (saleConfig.limitPerAccount == 0) { // Provide a more targeted error if the collection has not been listed. revert NFTDropMarketFixedPriceSale_Must_Have_Sale_In_Progress(); } revert NFTDropMarketFixedPriceSale_Cannot_Buy_More_Than_Limit(saleConfig.limitPerAccount); }
we use IERC721(nftContract).balanceOf(msg.sender) to check if the user bypass the limitPerAccount restriction,
https://stackoverflow.com/questions/72604383/how-to-create-a-limit-in-minting-using-balanceof-over-contract but user can buy nft, transfer nft out to another account, and buy more nft to bypass the restriction.
If you use the wallet balance you may not be able to correctly check the amount of mints made by a wallet, as the balance only tells you how many tokens the wallet currently has.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
For example, we set saleConfig.limitPerAccount to 2,
first, user buy two nft, second, user transfer two nft to his another wallet. third, user can buy two more nft.
I think you should create a mapping to store the amount of mints of each wallet. In your smart contract create a public mapping of mint amount of each wallet:
mapping(address => uint) public walletMints;
And then when the user mints you need add this amount to the address:
walletMints[msg.sender] = walletMints[msg.sender] + quantity_
When you create a public mapping the smart contract automatically creates a getter of this mapping that you can use in your javascript code and check the amount of mints made by a wallet.
#0 - 0xlgtm
2022-08-17T03:01:28Z
My issue here https://github.com/code-423n4/2022-08-foundation-findings/issues/59 with a proof of concept test in foundry but I disagree with severity as there are no loss of funds.
There are also too many dups for this issue so I won't list all of them down. Please check the de-dupe tool shared by kartoonjoy.
#1 - HardlyDifficult
2022-08-17T20:56:10Z
🌟 Selected for report: Saw-mon_and_Natalie
Also found by: 0x1f8b, 0x52, 0xDjango, 0xNazgul, 0xSmartContract, 0xSolus, 0xackermann, 0xmatt, 0xsolstars, Aymen0909, Bnke0x0, Chom, Deivitto, DevABDee, Dravee, ElKu, IllIllI, JC, Kumpa, Lambda, LeoS, MiloTruck, PwnedNoMore, ReyAdmirado, Rohan16, Rolezn, Ruhum, Sm4rty, TomJ, Treasure-Seeker, Vexjon, Waze, Yiko, __141345__, apostle0x01, auditor0517, berndartmueller, bin2chen, bobirichman, brgltd, bulej93, c3phas, carlitox477, cccz, cryptphi, csanuragjain, d3e4, danb, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, iamwhitelights, joestakey, jonatascm, ladboy233, mics, oyc_109, rbserver, ret2basic, robee, rokinot, rvierdiiev, shenwilly, sikorico, simon135, thank_you, wagmi, yash90, zeesaw, zkhorse
41.2563 USDC - $41.26
https://github.com/code-423n4/2022-08-foundation/blob/792e00df429b0df9ee5d909a0a5a6e72bd07cf79/contracts/mixins/nftDropMarket/NFTDropMarketFixedPriceSale.sol#L170 https://github.com/code-423n4/2022-08-foundation/blob/792e00df429b0df9ee5d909a0a5a6e72bd07cf79/contracts/mixins/shared/MarketFees.sol#L522
Detailed description of the impact of this finding.
When minting,
we go through
function mintFromFixedPriceSale( address nftContract, uint16 count, address payable buyReferrer ) external payable returns (uint256 firstTokenId) {
buyRefererer can be set to arbitrary address to the referral fee because when the referral fee is calcuulated.
if (buyReferrer != address(0) && buyReferrer != msg.sender && buyReferrer != seller && buyReferrer != creator) { unchecked { buyReferrerFee = totalFees / BUY_REFERRER_PROTOCOL_FEE_DENOMINATOR; // buyReferrerFee is always <= totalFees totalFees -= buyReferrerFee; } }
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
A user Bob wants to mint nft, he set Alice to receive the referral fee, but alice is bob's wallet address.
We can use another map to keep track of the referral.
mapping(address => address) referralMap;
and the function
function refer(address referTo) { referralMap[referTo] = msg.sender; }
and when a user is minting,
we can check if the referral address is correct.
function mintFromFixedPriceSale( address nftContract, uint16 count, address payable buyReferrer ) external payable returns (uint256 firstTokenId) { require(referralMap[msg.sender] == buyReferral, 'invalid referral') // other logic }
#0 - 0xlgtm
2022-08-17T02:36:18Z
Mitigation step does not actually mitigate sybil attack via referral fee since Bob can still set Alice address as a referral and he owns Alice's private key.
#1 - HardlyDifficult
2022-08-17T07:06:40Z
#2 - HickupHH3
2022-08-26T08:48:29Z
primary warden's QA report
🌟 Selected for report: Dravee
Also found by: 0x040, 0x1f8b, 0xDjango, 0xHarry, 0xNazgul, 0xSmartContract, 0xbepresent, 0xkatana, Amithuddar, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DevABDee, Diraco, ElKu, Fitraldys, Funen, IllIllI, JC, LeoS, Metatron, MiloTruck, Noah3o6, ReyAdmirado, Rohan16, Rolezn, Saw-mon_and_Natalie, Sm4rty, SpaceCake, TomJ, Tomio, Trabajo_de_mates, Waze, Yiko, __141345__, ajtra, apostle0x01, bobirichman, brgltd, bulej93, c3phas, cRat1st0s, carlitox477, d3e4, durianSausage, erictee, fatherOfBlocks, gerdusx, gogo, hakerbaya, ignacio, jag, joestakey, ladboy233, medikko, mics, newfork01, oyc_109, pfapostol, robee, rvierdiiev, sach1r0, saian, samruna, sikorico, simon135, wagmi, zeesaw, zkhorse, zuhaibmohd
20.5997 USDC - $20.60
getTokenCreatorPaymentAddress can be marked as external
can cache the .length,
for example,
uint256 length = array.length; for(uint256 i = 0; i < length; ++i) { logic }
#0 - HardlyDifficult
2022-08-19T15:23:12Z
Function can be marked as external
Invalid, this is used internally.
Cache Array Length Outside of Loop
May be theoretically valid, but won't fix. I tested this: gas-reporter and our gas-stories suite is reporting a small regression using this technique. It also hurts readability a bit so we wouldn't want to include it unless it was a clear win.