Platform: Code4rena
Start Date: 30/10/2023
Pot Size: $49,250 USDC
Total HM: 14
Participants: 243
Period: 14 days
Judge: 0xsomeone
Id: 302
League: ETH
Rank: 225/243
Findings: 1
Award: $0.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: smiling_heretic
Also found by: 00decree, 00xSEV, 0x180db, 0x3b, 0x656c68616a, 0xAadi, 0xAleko, 0xAsen, 0xDetermination, 0xJuda, 0xMAKEOUTHILL, 0xMango, 0xMosh, 0xSwahili, 0x_6a70, 0xarno, 0xgrbr, 0xpiken, 0xsagetony, 3th, 8olidity, ABA, AerialRaider, Al-Qa-qa, Arabadzhiev, AvantGard, CaeraDenoir, ChrisTina, DanielArmstrong, DarkTower, DeFiHackLabs, Deft_TT, Delvir0, Draiakoo, Eigenvectors, Fulum, Greed, HChang26, Haipls, Hama, Inference, Jiamin, JohnnyTime, Jorgect, Juntao, Kaysoft, Kose, Kow, Krace, MaNcHaSsS, Madalad, MrPotatoMagic, Neon2835, NoamYakov, Norah, Oxsadeeq, PENGUN, REKCAH, Ruhum, Shubham, Silvermist, Soul22, SovaSlava, SpicyMeatball, Talfao, TermoHash, The_Kakers, Toshii, TuringConsulting, Udsen, VAD37, Vagner, Zac, Zach_166, ZdravkoHr, _eperezok, ak1, aldarion, alexfilippov314, alexxander, amaechieth, aslanbek, ast3ros, audityourcontracts, ayden, bdmcbri, bird-flu, blutorque, bronze_pickaxe, btk, c0pp3rscr3w3r, c3phas, cartlex_, cccz, ciphermarco, circlelooper, crunch, cryptothemex, cu5t0mpeo, darksnow, degensec, dethera, devival, dimulski, droptpackets, epistkr, evmboi32, fibonacci, gumgumzum, immeas, innertia, inzinko, jasonxiale, joesan, ke1caM, kimchi, lanrebayode77, lsaudit, mahyar, max10afternoon, merlin, mrudenko, nuthan2x, oakcobalt, openwide, orion, phoenixV110, pontifex, r0ck3tz, rotcivegaf, rvierdiiev, seeques, shenwilly, sl1, slvDev, t0x1c, tallo, tnquanghuy0512, tpiliposian, trachev, twcctop, vangrim, volodya, xAriextz, xeros, xuwinnie, y4y, yobiz, zhaojie
0 USDC - $0.00
https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L31-L34 https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L104-L120 https://github.com/code-423n4/2023-10-nextgen/blob/main/smart-contracts/AuctionDemo.sol#L124-L130
The auctionDemo
contract holds the current auctions after the mintAndAuction functionality is called. Users can bid on a token and the highest bidder can claim the token after an auction finishes.
Users call participateToAuction()
to participate & place their bid in a auction & they can only participate if their bid is higher than the highest bid for that item. Once the auction finishes, the person with the highest bid & if their status is active/true can call claimAuction()
to claim their item.
However there is no restriction on what the starting price for an auction should be which makes it very easy for the attacker to win the auction & claim the prize but just giving some dust amount (1 wei) thereby causing a huge loss to the artist & organization.
Bidders call participateToAuction()
& if msg.value > returnHighestBid(_tokenid)
passes during the auction duration, they become the highest bidder until another buyer outbids their bid.
File: AuctionDemo function participateToAuction(uint256 _tokenid) public payable { require(msg.value > returnHighestBid(_tokenid) && block.timestamp <= minter.getAuctionEndTime(_tokenid) && minter.getAuctionStatus(_tokenid) == true); auctionInfoStru memory newBid = auctionInfoStru(msg.sender, msg.value, true); auctionInfoData[_tokenid].push(newBid); }
claimAuction()
has a modifier to ensure who can call the function i.e. the highest bidder, function admin or global admin to claim the NFT which goes to the highest bidder.
File: AuctionDemo modifier WinnerOrAdminRequired(uint256 _tokenId, bytes4 _selector) { require(msg.sender == returnHighestBidder(_tokenId) || adminsContract.retrieveFunctionAdmin(msg.sender, _selector) == true || adminsContract.retrieveGlobalAdmin(msg.sender) == true, "Not allowed"); _; }
Also a bidder can cancel their bid by calling cancelBid()
which changes their bidding status to false
.
File: AuctionDemo function cancelBid(uint256 _tokenid, uint256 index) public { require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); require(auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true); auctionInfoData[_tokenid][index].status = false; /// @note - status changes to false (bool success, ) = payable(auctionInfoData[_tokenid][index].bidder).call{value: auctionInfoData[_tokenid][index].bid}(""); emit CancelBid(msg.sender, _tokenid, index, success, auctionInfoData[_tokenid][index].bid); }
Although only one account is enough to carry out the attack but we are assuming attacker is using two accounts for better clarity.
participateToAuction()
with account X having 1 wei.auctionInfoData[_tokenid].length
is empty & returnHighestBid(_tokenid)
returns 0
in this case.participateToAuction()
with account Y which has a very huge amount of money.auctionInfoData
.cancelBid()
with the address of account Y.claimAuction()
which now checks the highest bid amount & bidder which is 1 wei & Bob respectively.File: AuctionDemo function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){ require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true); auctionClaim[_tokenid] = true; uint256 highestBid = returnHighestBid(_tokenid); /// @note - Returns 1 wei address ownerOfToken = IERC721(gencore).ownerOf(_tokenid); address highestBidder = returnHighestBidder(_tokenid); /// @note - returns Bob for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) { if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) { /// @note - check passes IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid); /// @note - NFT is received to Bob (bool success, ) = payable(owner()).call{value: highestBid}(""); /// @note - Owner is paid 1 wei emit ClaimAuction(owner(), _tokenid, success, highestBid); .........
Attacker can claim NFTs with dust amount causing a complete lost to the artist & the protocol.
Manual Review
Add a minimum starting auction amount to avoid this situation.
Error
#0 - c4-pre-sort
2023-11-15T10:31:10Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-02T15:13:15Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-02T15:16:57Z
alex-ppg marked the issue as duplicate of #1784
#3 - c4-judge
2023-12-07T11:49:36Z
alex-ppg marked the issue as duplicate of #1323
#4 - c4-judge
2023-12-08T17:27:00Z
alex-ppg marked the issue as partial-50
#5 - c4-judge
2023-12-08T17:28:20Z
alex-ppg marked the issue as satisfactory
#6 - c4-judge
2023-12-08T18:22:30Z
alex-ppg marked the issue as partial-50