NextGen - jasonxiale's results

Advanced smart contracts for launching generative art projects on Ethereum.

General Information

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

NextGen

Findings Distribution

Researcher Performance

Rank: 216/243

Findings: 1

Award: $0.00

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 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

Awards

0 USDC - $0.00

Labels

bug
3 (High Risk)
satisfactory
sponsor confirmed
upgraded by judge
duplicate-1323

External Links

Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L112

Vulnerability details

Impact

calling safeTransferFrom in AuctionDemo.claimAuction contains a reentrancy vulnerability, by abusing safeTransferFrom, the malicious bidder can get the token without paying any ETH

Proof of Concept

To abusing the reentrancy vulnerability, 3 conditions need to be met:

  1. safeTransferFrom has a callback
  2. AuctionDemo.claimAuction doesn't have reentrancy protection, and can be called when block.timestamp >= minter.getAuctionEndTime(_tokenid), please note here the comparison is >=
  3. AuctionDemo.cancelBid doesn't have reentrancy protection, and can be called when block.timestamp <= minter.getAuctionEndTime(_tokenid), please note here the comparison is <=

Suppose Alice depolys a contract to participate in the auction, and the contract is the highest bidder, and she calls AuctionDemo.claimAuction when (block.timestamp == minter.getAuctionEndTime(_tokenid), the function will pass require check at AuctionDemo.sol#L105, and then will arrive AuctionDemo.sol#L112, within safeTransferFrom, contract's onERC721Received will be called

104     function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){
105         require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true); <<<--- we call the function when block.timestamp == minter.getAuctionEndTime(_tokenid)
106         auctionClaim[_tokenid] = true;
107         uint256 highestBid = returnHighestBid(_tokenid);
108         address ownerOfToken = IERC721(gencore).ownerOf(_tokenid);
109         address highestBidder = returnHighestBidder(_tokenid);
110         for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) {
111             if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) {
112                 IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid); <<< --- Here will call highestBidder.onERC721Received
113                 (bool success, ) = payable(owner()).call{value: highestBid}("");
114                 emit ClaimAuction(owner(), _tokenid, success, highestBid);
115             } else if (auctionInfoData[_tokenid][i].status == true) {
116                 (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}("");
117                 emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid);
118             } else {}
119         }
120     }

Within the onERC721Received function, the contract will call AuctionDemo.cancelBid or AuctionDemo.cancelAllBids. In AuctionDemo.cancelBid, the require will be passed because we're calling when block.timestamp == minter.getAuctionEndTime(_tokenid)

124     function cancelBid(uint256 _tokenid, uint256 index) public {
125         require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); <<<--- we're calling when block.timestamp == minter.getAuctionEndTime(_tokenid)
126         require(auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true);
127         auctionInfoData[_tokenid][index].status = false;
128         (bool success, ) = payable(auctionInfoData[_tokenid][index].bidder).call{value: auctionInfoData[_tokenid][index].bid}("");
129         emit CancelBid(msg.sender, _tokenid, index, success, auctionInfoData[_tokenid][index].bid);
130     }

after calling AuctionDemo.cancelBid, Alice gets her ETH back, and as AuctionDemo.claimAuction continue, she also gets the auction token.

Tools Used

VIM

add reentrancy protection

Assessed type

Reentrancy

#0 - c4-sponsor

2023-11-24T09:22:27Z

a2rocket (sponsor) confirmed

#1 - c4-judge

2023-12-04T21:33:25Z

alex-ppg marked the issue as duplicate of #1547

#2 - c4-judge

2023-12-04T21:42:00Z

alex-ppg marked the issue as duplicate of #1323

#3 - c4-judge

2023-12-08T17:46:55Z

alex-ppg marked the issue as satisfactory

#4 - c4-judge

2023-12-09T00:20:29Z

alex-ppg changed the severity to 3 (High Risk)

Findings Information

🌟 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

Awards

0 USDC - $0.00

Labels

bug
3 (High Risk)
satisfactory
sponsor disputed
sufficient quality report
upgraded by judge
duplicate-1323

External Links

Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L116

Vulnerability details

Impact

Calling payable().call in AuctionDemo.claimAuction contains a reentrancy vulnerability, by abusing it, the malicious bidder can get the token without paying any ETH. Please note the issue can be exploited other non-highest bidders as the following 3 conditions are met.

Proof of Concept

To abusing the reentrancy vulnerability, 3 conditions need to be met:

  1. payable().call has a callback
  2. AuctionDemo.claimAuction doesn't have reentrancy protection, and can be called when block.timestamp >= minter.getAuctionEndTime(_tokenid), please note here the comparison is >=
  3. AuctionDemo.cancelBid doesn't have reentrancy protection, and can be called when block.timestamp <= minter.getAuctionEndTime(_tokenid), please note here the comparison is <=

Suppose Alice is the highest bidder, and she calls AuctionDemo.claimAuction when (block.timestamp == minter.getAuctionEndTime(_tokenid), the function will pass require check at AuctionDemo.sol#L105, and then will arrive AuctionDemo.sol#L116 to send the ETHs back to other bidders. Since payable().call is used here, if the bidder is a contract and contains a fallback function, the fallback function will be called.

104     function claimAuction(uint256 _tokenid) public WinnerOrAdminRequired(_tokenid,this.claimAuction.selector){
105         require(block.timestamp >= minter.getAuctionEndTime(_tokenid) && auctionClaim[_tokenid] == false && minter.getAuctionStatus(_tokenid) == true); <<<--- we call the function when block.timestamp == minter.getAuctionEndTime(_tokenid)
106         auctionClaim[_tokenid] = true;
107         uint256 highestBid = returnHighestBid(_tokenid);
108         address ownerOfToken = IERC721(gencore).ownerOf(_tokenid);
109         address highestBidder = returnHighestBidder(_tokenid);
110         for (uint256 i=0; i< auctionInfoData[_tokenid].length; i ++) {
111             if (auctionInfoData[_tokenid][i].bidder == highestBidder && auctionInfoData[_tokenid][i].bid == highestBid && auctionInfoData[_tokenid][i].status == true) {
112                 IERC721(gencore).safeTransferFrom(ownerOfToken, highestBidder, _tokenid);
113                 (bool success, ) = payable(owner()).call{value: highestBid}("");
114                 emit ClaimAuction(owner(), _tokenid, success, highestBid);
115             } else if (auctionInfoData[_tokenid][i].status == true) {
116                 (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}(""); <<< --- Here will call bidder's fallback function if the bidder has one
117                 emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid);
118             } else {}
119         }
120     }

Within the fallback function, the contract will call AuctionDemo.cancelBid or AuctionDemo.cancelAllBids. In AuctionDemo.cancelBid, the require will be passed because we're calling when block.timestamp == minter.getAuctionEndTime(_tokenid)

124     function cancelBid(uint256 _tokenid, uint256 index) public {
125         require(block.timestamp <= minter.getAuctionEndTime(_tokenid), "Auction ended"); <<<--- we're calling when block.timestamp == minter.getAuctionEndTime(_tokenid)
126         require(auctionInfoData[_tokenid][index].bidder == msg.sender && auctionInfoData[_tokenid][index].status == true);
127         auctionInfoData[_tokenid][index].status = false;
128         (bool success, ) = payable(auctionInfoData[_tokenid][index].bidder).call{value: auctionInfoData[_tokenid][index].bid}("");
129         emit CancelBid(msg.sender, _tokenid, index, success, auctionInfoData[_tokenid][index].bid);
130     }

In such way, a malicious bidder can get double ETH of his bids.

Tools Used

Assessed type

Reentrancy

#0 - a2rocket

2023-11-24T09:22:46Z

duplicate

#1 - c4-sponsor

2023-11-24T09:22:47Z

a2rocket (sponsor) disputed

#2 - c4-pre-sort

2023-11-27T15:04:11Z

141345 marked the issue as sufficient quality report

#3 - c4-pre-sort

2023-11-27T15:14:12Z

141345 marked the issue as duplicate of #962

#4 - c4-judge

2023-12-04T21:42:01Z

alex-ppg marked the issue as duplicate of #1323

#5 - c4-judge

2023-12-08T17:46:41Z

alex-ppg marked the issue as satisfactory

#6 - c4-judge

2023-12-09T00:20:29Z

alex-ppg changed the severity to 3 (High Risk)

Findings Information

🌟 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

Awards

0 USDC - $0.00

Labels

bug
3 (High Risk)
partial-50
upgraded by judge
duplicate-1323

External Links

Lines of code

https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L104-L120 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L124-L130 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L134-L143

Vulnerability details

Impact

A malicious bidder, essentially the validator/MEVer who has the ability to slip in transaction can back-run AuctionDemo.claimAuction to steal ETH.

Proof of Concept

To exploit this issue, it requires that AuctionDemo.claimAuction must be called when block.timestamp == minter.getAuctionEndTime(_tokenid) In such case, when the highest bidder claim his auction, all other bidders' ETH will be sent back by AuctionDemo.sol#L116. But in AuctionDemo.claimAuction the function doesn't set auctionInfoData[_tokenid][index].status = false; as AuctionDemo.cancelBid does.

    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; <<<------- auctionInfoData.status is set to false, so this funciton can't be called again.
        (bool success, ) = payable(auctionInfoData[_tokenid][index].bidder).call{value: auctionInfoData[_tokenid][index].bid}("");
        emit CancelBid(msg.sender, _tokenid, index, success, auctionInfoData[_tokenid][index].bid);
    }

Because of lack of setting auctionInfoData[_tokenid][index].status, it opens door to malicious bidder, he can back-run AuctionDemo.claimAuction by calling AuctionDemo.cancelBid or AuctionDemo.cancelAllBids in the same block, in such case, the require won't revert because of the tx is still under the condition of block.timestamp == minter.getAuctionEndTime(_tokenid) By doing so, the malicious bidder can steal ETH

Tools Used

VIM

diff --git a/hardhat/smart-contracts/AuctionDemo.sol b/hardhat/smart-contracts/AuctionDemo.sol
index 95533fb..74cf250 100644
--- a/hardhat/smart-contracts/AuctionDemo.sol
+++ b/hardhat/smart-contracts/AuctionDemo.sol
@@ -113,6 +113,7 @@ contract auctionDemo is Ownable {
                 (bool success, ) = payable(owner()).call{value: highestBid}("");
                 emit ClaimAuction(owner(), _tokenid, success, highestBid);
             } else if (auctionInfoData[_tokenid][i].status == true) {
+                auctionInfoData[_tokenid][i].status = false;
                 (bool success, ) = payable(auctionInfoData[_tokenid][i].bidder).call{value: auctionInfoData[_tokenid][i].bid}("");
                 emit Refund(auctionInfoData[_tokenid][i].bidder, _tokenid, success, highestBid);
             } else {}
@@ -148,4 +149,4 @@ contract auctionDemo is Ownable {
         return auctionInfoData[_tokenid];
     }
 
-}
\ No newline at end of file
+}

Assessed type

MEV

#0 - c4-pre-sort

2023-11-15T05:18:51Z

141345 marked the issue as duplicate of #962

#1 - c4-judge

2023-12-01T14:46:37Z

alex-ppg marked the issue as not a duplicate

#2 - c4-judge

2023-12-01T14:46:46Z

alex-ppg marked the issue as duplicate of #1788

#3 - c4-judge

2023-12-08T17:46:03Z

alex-ppg marked the issue as partial-50

#4 - c4-judge

2023-12-09T00:20:29Z

alex-ppg changed the severity to 3 (High Risk)

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