Foundation contest - csanuragjain's results

Building the new creative economy

General Information

Platform: Code4rena

Start Date: 24/02/2022

Pot Size: $75,000 USDC

Total HM: 21

Participants: 28

Period: 7 days

Judge: alcueca

Total Solo HM: 15

Id: 94

League: ETH

Foundation

Findings Distribution

Researcher Performance

Rank: 19/28

Findings: 2

Award: $539.51

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Awards

336.9655 USDC - $336.97

Labels

bug
QA (Quality Assurance)

External Links

Stuck Seller NFT

  1. In placeBidOf function at NFTMarketReserveAuction.sol#L386, attacker can keep on extending the auction time and hence seller nft would stuck
  2. User A places his NFT for Auction with price 1
  3. Malicious User B who dont want this NFT to be sold, simply places bid on this NFT for say amount 1
  4. Once auction is nearing the end, User B immediately places another bid of amount 1+10% which increases auction time by extensionDuration (15 min) and releases amount from step 3
  5. User B keeps on repeating Step 4 once extensionDuration is near to end (14 minutes) using bot
  6. Since auction cannot end due to continuos bid and there is no way seller can force close auction, seller nft gets stuck
  7. Only way left for seller is to contact Admin and make him call adminCancelReserveAuction function which will not be easy task

Note: This attack only works if initial amount set by seller is low

Migrate auctions which were not authorized by seller

  1. In adminAccountMigration function at NFTMarketReserveAuction.sol#L263, Since Auction id is independent of signature, FoundationOperator can provide an auction id which seller has not authorized the foundation operator
  2. User A requested foundation operator to change address for auction id 1
  3. foundation operator calls adminAccountMigration and instead of just passing auction id 1 he also passes auction id 2 owned by user A
  4. Since signature is independent of account id, so migration gets success

Bypass 24 hour sale window

  1. The buyFromPrivateSaleFor function at NFTMarketPrivateSale.sol#L123 checks whether sale has expired (24-25 hours passed) or not
  2. Assume User A started sale with deadline of 5 days instead of 1 day
  3. buyFromPrivateSaleFor is called on 4th day
  4. deadline > block.timestamp + 2 days is checked which returns false since 5 days > 4+2 days which is false and hence NFTMarketPrivateSale_Can_Be_Offered_For_24Hrs_Max is bypassed

Recommendation: Instead of using block.timestamp + 2 days, use the date when signature was requested

Sanity checks missing

  1. In adminCancelOffers function at NFTMarketOffer.sol#L150, make sure to check if nftContracts.length=tokenIds.length
require(nftContracts.length==tokenIds.length, "Incorrect Input");
  1. The depositFor function at FETH.sol#L232 should add a check to see account!=address(0), otherwise user can mistakenly send money to zero (burn) address
require(account!=address(0), "Invalid address");

approve could be frontrun

  1. The approve function in FETH.sol could be frontrun

#0 - HardlyDifficult

2022-03-02T16:51:53Z

Thanks for the feedback, these are good suggestions.

  • Auction stuck: This is okay. Yes someone could drag an auction on for a long time if the price starts at 1 wei, however all it would take is one "real" bid to place and the game is over. Eventually the attacker will not be able to afford to continue and in the end the seller wins as the price continues to climb. Additionally the gas costs of this sort of attack would be a huge deterrent.
  • Migration not authorized: We believe this report is not correct and things are okay as they are. We do have a check that confirms the auction.seller == originalAddress before processing the request.
  • 24 hour sale window: This could still lead to a similar problem. e.g. a phishing attack could collect signatures using a date sometime in the future. It's still easy for user's to overlook that detail. This change may be an improvement as it's slightly easier to parse, but we are going to not make a change here in order to preserve backwards compatibility with the current frontend integration and any signatures which are outstanding at the time of our upgrade.
  • Sanity checks missing: A duplicate report, but correct and we have made the fixes suggested.
  • Approval could be frontrun: A duplicate report, we are actively discussing next steps on this one now. We agree with the recommendation but there's a complicating factor related to another contract we use that makes this decision a bit less clear.

#1 - alcueca

2022-03-17T09:41:05Z

Unadjusted score: 40

#2 - alcueca

2022-03-17T10:02:58Z

+5 points from #34

Findings Information

🌟 Selected for report: Dravee

Also found by: 0x1f8b, CertoraInc, TerrierLover, csanuragjain, gzeon, iain, kenta, rfa, robee, thankthedark

Labels

bug
G (Gas Optimization)

Awards

202.5376 USDC - $202.54

External Links

  1. In buyFromPrivateSaleFor function at NFTMarketPrivateSale.sol#L180, _distributeFunds function is called and returned variable are stored in (uint256 f8nFee, uint256 creatorFee, uint256 ownerRev). However none of these variables are actually used. Recommendation would be to just call _distributeFunds function without storing any returned value from the function

  2. In _getCreatorPaymentInfo function at NFTMarketCreators.sol#L49, observe that code at L90-L106 is similar to L153-L166. An ideal way would be to create a function of the recurring code and then call these functions without rewriting them multiple times

  3. In _transferFromEscrow function at NFTMarketOffer.sol#L296, use memory instead of storage in below line:

Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];
  1. In _cancelBuyersOffer function at NFTMarketOffer.sol#L326, use memory instead of storage in below line:
Offer storage offer = nftContractToIdToOffer[nftContract][tokenId];

#0 - HardlyDifficult

2022-03-01T11:52:21Z

Thanks for the feedback!

  • Unused variables: These return values are actually used to emit the event that comes after. We use these events to track how the revenue was distributed from each sale.
  • Reuse code in _getCreatorPaymentInfo: This function is intense. We are discussing ways we might be able to simplify the whole thing. When we do this, we'll consider extracting some helpers to remove redundancies as well.
  • Use memory in _transferFromEscrow: Related to a recent change, we have removed this function from the NFTMarketOffer file so this no longer applies.
  • Use memory in _cancelBuyersOffer: We tested this and it actually seems to increase gas costs a bit. As a general best practice we are aiming to use storage everywhere possible and limit memory to when we need to cache the original value (e.g. if we are going to delete the struct and then emit the original values afterwards). storage is often cheaper since it'll only read the slots that are needed for the logic executed while memory will read all the storage slots used by that struct. This is more impactful in a class like our ReserveAuctions since that struct uses several slots.

#1 - alcueca

2022-03-17T15:41:50Z

Score: 300

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter