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: 168/243
Findings: 2
Award: $0.47
🌟 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/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L104 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L124
Due to improper two aspects, there's a scenario where cancelBid and claimAuction are both callable while it always should be one of the two.
claimAuction()
has the following require statement require(block.timestamp >= minter.getAuctionEndTime(_tokenid))
cancelBid()
has the following require(block.timestamp <= minter.getAuctionEndTime(_tokenid))
This means that if block.timestamp == minter.getAuctionEndTime(_tokenid)
both functions will be activated.
When claimAuction()
is called auctionInfoData[_tokenid][i].status
is never set to false like it would in cancelBid()
.
This results in the attack vector where cancelBid()
is called after claimAuction()
in the same block.timestamp.
This could lead to 1. the winner getting the NFT and getting his bid back 2. a bidder receiving his bid twice.
An attacker could check the current balance of the contract, deposit the same amount and withdraw that amount * 2 by above mentioned attack.
block.timestamp == 100
claimAuction()
is called where the following passes block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true
cancelBid()
transaction and ensure it is mined in the same timestampcancelBid()
passes the following requirements block.timestamp <= minter.getAuctionEndTime(_tokenid)
and auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true
since the status is never set to false in claimAuction()
manual review
For this specific issue either fixing cause 1 or 2 will fix this problem. The timestamp checks needs to be checked throughout the whole codebase. I would recommend:
Either end actions at the endtime or after the endtime throughout the whole codebase. Example:
endOfAuction == endTime
here, when block.timestamp hits endTime
, the auction would be ended. OR:
endOfAcution > endTime
here, when block.timestamp hits endTime
, the auction is still open and would end when that specific time has been PASSED.
set blocktimestamp requirement of AuctionDemo.sol
to: require(block.timestamp <= minter.getAuctionEndTime(_tokenid)
in claimAcution()
, set auctionInfoData[_tokenid][index].status
to false BEFORE sending back the ETH or NFT
Invalid Validation
#0 - c4-pre-sort
2023-11-15T07:42:02Z
141345 marked the issue as duplicate of #1172
#1 - c4-judge
2023-12-06T21:28:10Z
alex-ppg marked the issue as duplicate of #1323
#2 - c4-judge
2023-12-08T18:05:02Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: The_Kakers
Also found by: 00decree, 00xSEV, 0x180db, 0x3b, 0xJuda, 0x_6a70, 0xarno, 0xpiken, Arabadzhiev, Bauchibred, BugsFinder0x, BugzyVonBuggernaut, ChrisTina, DeFiHackLabs, Delvir0, HChang26, Haipls, Jiamin, Juntao, KupiaSec, Madalad, Neon2835, Nyx, Ocean_Sky, SpicyMeatball, Talfao, Taylor_Webb, Timenov, Tricko, ZdravkoHr, _eperezok, alexxander, amaechieth, bdmcbri, bronze_pickaxe, circlelooper, crunch, cu5t0mpeo, dimulski, fibonacci, funkornaut, immeas, ke1caM, lsaudit, nuthan2x, r0ck3tz, rotcivegaf, spark, tnquanghuy0512, twcctop, xeros
0.4703 USDC - $0.47
Note: 1. this requires the attacker to lose his highest bid and lose out on the NFT 2. unlike the bot report this is not due to the loop DOS.
When the auction is over, claimAuction()
loops over the bids and either 1. sends the NFT to the highest bidder and 2. returns the bids to the participants.
However, if the highest bidder reverts the transfer on purpose, no one will be able to get their bids value back. This is due to the fact that when calling the function, it will loop over all bids and eventually end up in the transfer call.
claimAuction()
, it will always revertManual review
There are two ways to handle this.
claimAuction()
without the modifier and where the transfer only occurs if msg.sender == auctionInfoData[_tokenid][i].bidder
DoS
#0 - c4-pre-sort
2023-11-19T15:03:49Z
141345 marked the issue as duplicate of #486
#1 - c4-judge
2023-12-01T22:42:58Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-01T22:43:21Z
alex-ppg marked the issue as duplicate of #1759
#3 - c4-judge
2023-12-08T22:14:34Z
alex-ppg marked the issue as partial-50