NextGen - Vagner'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: 141/243

Findings: 2

Award: $2.77

🌟 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)
partial-50
duplicate-1323

External Links

Lines of code

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

Vulnerability details

Impact

The auction can be reentered, in some specific situations, which would cause a bidder to get back more ether than it should from the contract.

Proof of Concept

The bid system allows an user that bided already to get his share back by calling cancelBid or cancelAllBids if he bided multiple times. Both of them check if block.timestamp is <= than getAuctionEndTime as can be seen here https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L125 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L135 and if that is the case, it sets the status of the bidder to false https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L127 https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L138 so when claimAuction is called, the iteration would pass over the users that already cancel and claim their bids. The problem relies in the fact that claimAuction checks for block.timestamp to be >= than getAuctionEndTime while cancelBid and cancelAllBids check for block.timestamp to be <= than getAuctionEndTime so there is a small timeframe where both statements would be true, when block.timestamp is == to getAuctionEndTime. In that scenario, when claimAuction is called, because block.timestamp is the same trough a whole transaction, a malicious user could have bided with a contract that looks like this

contract MaliciousBidder {
    uint256 tokenID;
    uint256 index;
    address AuctionAddress;

    function participate(uint256 tokenId, address x) public payable {
        Auction(x).participateToAuction{value : msg.value}(tokenId);
    }

    receive () payable external {
        Auction(AuctionAddress).cancelBid(tokenID,index);
    }
}

which would basically call the cancelBid function and all of the checks would pass since block.timestamp would be == to getAuctionEndTime https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L125 msg.sender would be the bidder and the status is still set to true, since it is not changed in the claimAuction function https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L126 In that way the bidder would get twice the amount of ether that he is supposed to, which would make other bidders lose their funds. Since the claimAuction can be called also by the highest bidder, he could call the function exactly at block.timestamp == minter.getAuctionEndTime(_tokenid), in case he bided multiple times, so he can get more ether back thatn it should, so this makes it easily abusable.

Tools Used

Manual review

There are multiple ways to fix this, you can :

  • use a reentrancy protection to the functions
  • set the status of the bidders to false before you sent them the ether in the claimAuction function
  • check only for block.timestamp to be only < or > without the = sign, so the vulnerability is not achievable

Assessed type

Reentrancy

#0 - c4-pre-sort

2023-11-14T13:50:15Z

141345 marked the issue as duplicate of #289

#1 - c4-pre-sort

2023-11-14T23:32:25Z

141345 marked the issue as duplicate of #962

#2 - c4-judge

2023-12-04T21:41:04Z

alex-ppg marked the issue as duplicate of #1323

#3 - c4-judge

2023-12-08T18:05:30Z

alex-ppg marked the issue as satisfactory

#4 - c4-judge

2023-12-08T18:25:46Z

alex-ppg marked the issue as partial-50

Lines of code

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

Vulnerability details

Impact

The way the auction it is implemented right now in AuctionDemo.sol can be easily DoS'ed blocking the whole claimAuction mechanism.

Proof of Concept

The AuctionDemo.sol is used to auction an NFTcreated in the MinterContract.sol by calling mintAndAuction function. After that, anyone can bid on it by calling participateToAuction https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L57-L61 where you need to pay more than the highest bidder, to get your data saved in the auctionInfoStru. After the auction ends the highest bidder or the admin can call claimAuction to iterate over the whole array of auctionInfoStru saved, and transfer the NFT from the owner to the highest bidder and also transfer the ether sent by any other bidder back to themselves, as can be see here https://github.com/code-423n4/2023-10-nextgen/blob/8b518196629faa37eae39736837b24926fd3c07c/smart-contracts/AuctionDemo.sol#L110-L119 The problem relies in the fact that this can be easily abusable, by forcing an OOG error and blocking the whole mechanism. To give you an example, I can participate in the early stages of auction, to pay a very low amount of ether, with a contract created by me which looks like this

contract MaliciousBidder {
    uint256[2000] public arr;

    function participate(uint256 tokenId, address x) public payable {
        Auction(x).participateToAuction{value : msg.value}(tokenId);
    }

    receive () payable external {
        // simulate a very expensive operation
        for (uint256 index = 0; index < 2000; index++) {
            arr[index] = index + 1;
        }
    }
}

and when the claimAuction function is called, because it iterates and tries to transfer the balances to every bidder, this would cost a lot of gas, more than the gas limit of the block which would force an OOG and block the auction forever. To give you some data about it, I tested with and array of 200 items, and that would cost around 4 million gas, only for the receive function, considering others operations/iterations done in the claimAuction this would cause and OOG.

Tools Used

Manual review

My recommendation would be to use the Pull-over-Push method, let every bidder claim his share, and only transfer the NFT to the highest bidder and the ether to the owner in the claimAuction, in that way you can't force and block the whole functionality of the contract.

Assessed type

DoS

#0 - c4-pre-sort

2023-11-16T10:37:52Z

141345 marked the issue as duplicate of #1952

#1 - c4-judge

2023-12-04T19:03:54Z

alex-ppg marked the issue as not a duplicate

#2 - c4-judge

2023-12-04T19:04:05Z

alex-ppg marked the issue as duplicate of #1782

#3 - c4-judge

2023-12-08T20:51:27Z

alex-ppg marked the issue as satisfactory

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