Platform: Code4rena
Start Date: 20/09/2022
Pot Size: $100,000 USDC
Total HM: 4
Participants: 109
Period: 7 days
Judge: GalloDaSballo
Id: 163
League: ETH
Rank: 15/109
Findings: 2
Award: $1,913.41
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: philogy
Also found by: KIntern_NA, auditor0517, bin2chen, cccz, hansfriese, hyh, ladboy233, m9800, pauliax, pedroais, ronnyx2017, wagmi, wastewa, zzykxx
1858.2053 USDC - $1,858.21
https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/ArtGobblers.sol#L441 https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/ArtGobblers.sol#L880
Legendary gobblers can be minted for free.
A simple attack allows anyone with enough gobblers to pay for a legendary to get it for free
Step by step attack :
-Attacker has 69 gobblers and gives approval of all of them to himself https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/utils/token/GobblersERC721.sol#L92
-Attacker burns his 69 gobblers to get the legendary gobbler at the start of the dutch auction
-Ownership of the 69 gobblers is set to 0 but approval data is never deleted https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/ArtGobblers.sol#L441
-Attacker calls transferFrom(address(0),attacker, ID) for each of the 69 involved ID's
-This doesn't fail since msg.sender == getApproved[id] will be true and there isn't a from != address(0) check like there is for "to" https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/ArtGobblers.sol#L890
Attacker now has the legendary gobbler and his 69 gobblers
Delete approval data. This is correctly done in the transfer from function but not done when setting the owner to address(0) in mintLegendaryGobbler().
Disallowing transferFrom with from == address(0) is also a solution
#0 - Shungy
2022-09-27T18:49:13Z
#1 - GalloDaSballo
2022-10-02T14:12:52Z
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0x52, 0x5rings, 0xNazgul, 0xRobocop, 0xSmartContract, 0xdeadbeef, 0xsanson, 8olidity, Amithuddar, Aymen0909, B2, B353N, CertoraInc, Ch_301, Chom, CodingNameKiki, Deivitto, ElKu, Funen, JC, JohnnyTime, Kresh, Lambda, Noah3o6, RaymondFam, ReyAdmirado, RockingMiles, Rolezn, Sm4rty, SuldaanBeegsi, Tadashi, TomJ, Tomio, V_B, Waze, __141345__, a12jmx, ak1, arcoun, asutorufos, aviggiano, berndartmueller, bharg4v, bin2chen, brgltd, bulej93, c3phas, catchup, cccz, ch0bu, cryptonue, cryptphi, csanuragjain, delfin454000, devtooligan, djxploit, durianSausage, eighty, erictee, exd0tpy, fatherOfBlocks, giovannidisiena, hansfriese, ignacio, joestakey, ladboy233, lukris02, m9800, malinariy, martin, minhtrng, obront, oyc_109, pedr02b2, pedroais, pfapostol, philogy, prasantgupta52, rbserver, ronnyx2017, rotcivegaf, rvierdiiev, sach1r0, shung, simon135, throttle, tnevler, tonisives, wagmi, yixxas, zkhorse, zzykxx, zzzitron
55.1985 USDC - $55.20
The codebase is an very well optimized and secured one that follows general good practices. Nevertheless some low / informational issues can still be improved.
Low : 1- Race condition when minting legendary Gobbler
Dutch auctions have a problem in the blockchain. Since frontrunning exists if 2 users want to buy at a determined price the winner will be the highest gas payer or the one with more validation power. In a regular auction, the address that wants to win it the most could pay more. In a dutch auction, there is a race condition: the first tx to be included will win the auction. So if multiple users want to buy the auction at the start price the winner will be decided solely on gas paid and validation power.
2- Not using safeMint If the recipient is a contract, safeMint() checks whether they can handle ERC721 tokens which prevents from unwanted NFT's being bricked inside a contract. https://github.com/code-423n4/2022-09-artgobblers/blob/d2087c5a8a6a4f1b9784520e7fe75afa3a9cbdbe/src/ArtGobblers.sol#L356
This is correctly implemented in the contract's safeTransferFrom function but not implemented when minting.
Informational :
1- The gooble function only allows the owner to feed the gobble. The gobble() function should support feeding by approved non-owner addresses. These addresses already have the power to do it but in the current implementation, they would have to transfer it to themselves first.
Instead of owner!=msg.sender it should be msg.sender == owner|| isApprovedForAll[owner][msg.sender] || msg.sender == getApproved[id],
2- Lack of input checks The constructor should check _mintStart isn't in the past and critical addresses are not addresses(0)
3- Using an unset pragma.
Files use pragma solidity >=0.8.0. This should be changed to a fixed pragma to ensure the compiler will run in the same way if the contracts are deployed on a newer solidity version
4- It's a good practice to declare enums and structs at the top of the contract before functions
#0 - GalloDaSballo
2022-10-06T20:30:04Z
I don't think I can mark this as valid as it misses the point of the VRGDA
L
R
L
NC
4- It's a good practice to declare enums and structs at the top of the contract before functions
NC
2L 1R 2NC