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: 64/243
Findings: 3
Award: $98.50
🌟 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
In the AuctionDemo.sol
, the claimAuction
function has a big flaw that allows any losing bidder to steal part of the owner ether, steal the nft or make the owner unable to receive the ether of the auction.
In the claimAuction
function, when the winner or an admin calls it, it is coded to send the funds back to every bidder that lost the auction, but it doesn't change the status of the bid to false.
Since it is possible to call claimAuction
and cancelBid
if the block.timestamp is equal to the auctionEndTime
, an attacker is able to double the eth from a losing bid by calling cancelBid
when the external call from claimAuction
triggers on his contract.
It is also possible for the attacker to guarantee the win of the nft and pay nothing for it by making two bids at the auctionEndTime
timestamp and then calling the claimAuction
function, and when receiving the eth for the losing bid, call cancelBid
on it to double it and making the attacker recover all his funds and win the auction.
I'd consider it's impact to be high since an attacker can gain eth or an nft that should not belong to him, and even end up locking funds forever.
An auction opens and Alice bids 1 eth
Auction current winner: Alice Contract balance: 1 eth NFT owner: owner()
On the auctionEndTime
timestamp, attacker makes two bids and then calls claimAuction
.
First bid: 1 eth + 1 gwei
Auction current winner: Attacker Contract balance: 2 eth + 1 gwei NFT owner: owner()
Second bid: 1 eth + 2 gwei
Auction current winner: Attacker Contract balance: 3 eth + 3 gwei NFT owner: owner()
claimAuction
is called, Alice is refunded.
Auction current winner: Attacker Contract balance: 2 eth + 3 gwei NFT owner: owner()
When claimAuction
refunds the First bid from the attacker, it triggers the attacker receive
function
Auction current winner: Attacker Contract balance: 1 eth + 2 gwei NFT owner: owner()
The attacker receive
function calls cancelBid
for the lost bid.
Auction current winner: Attacker Contract balance: 0 eth + 1 gwei NFT owner: owner()
claimAuction
transfers ownership of the NFT.
Auction current winner: Attacker Contract balance: 0 eth + 1 gwei NFT owner: Attacker
Starting balances:
Attacker balance: 2 ether Alice balance: 1 ether
An auction opens and Alice tries to bid 1 eth and sends the tx to a public mempool, an attacker spots this and places a transaction just before Alice with a bid of 1 ether - 1 gwei.
Auction current winner: Alice Contract balance: 2 eth - 1 gwei NFT owner: owner() Attacker balance: 1 ether + 1 gwei Alice balance: 0 ether
On the auctionEndTime
timestamp, attacker makes one winner bid of 1 ether + 1 gwei and then calls claimAuction
.
Auction current winner: Attacker Contract balance: 3 eth NFT owner: owner() Attacker balance: 0 ether Alice balance: 0 ether
When the contract refunds the attackers first bid, it triggers his receive
function.
Auction current winner: Attacker Contract balance: 2 eth + 1 gwei NFT owner: owner() Attacker balance: 1 ether -1 gwei Alice balance: 0 ether
The attacker receive
function calls cancelAllBids
, getting a refund of the first 1 eth - 1 gwei bid again, and canceling the winning bid of 1 eth + 1 gwei.
Auction current winner: Attacker Contract balance: 1 gwei NFT owner: owner() Attacker balance: 3 ether -1 gwei Alice balance: 0 ether
claimAuction
tries to refund Alice, but it is not able to send any ether as there is no more ether left. The transaction doesn't revert since the boolean return of the .call is not checked, just emited.
Auction current winner: Attacker Contract balance: 1 gwei NFT owner: owner() Attacker balance: 3 ether -1 gwei Alice balance: 0 ether
claimAuction
checks if the winner bid is still active, it detects it isn't so it doesn't do anything and the function ends.
This makes the auction end with no winner but the actual winner ends up with no NFT and no ETH.
Auction current winner: No one Contract balance: 1 gwei NFT owner: owner() Attacker balance: 3 ether -1 gwei Alice balance: 0 ether
This attack pattern permits the attacker to steal the ether of the winning bid by always placing a bid just 1 gwei lower right before the bid, and performing the attack at the desired timestamp.
There is no risk for the attacker since he can make the winner bid to only go through if the timestamp is equal to auctionEndTime
, and if it doesn't work the attacker just gets refunded the losing bid.
The maximum amount that can be stolen by an attacker is equal to the attackers ether amount, but since the winner bid and the double refund happens on the same tx, the attacker can use a flash loan to guarantee winning the auction and being able to call claimAuction
.
VSCode
Make sure one of the two functions claimAuction
and cancelBid
is not able to be called when the block.timestamp is equal to auctionEndTime
.
Reentrancy
#0 - c4-pre-sort
2023-11-14T23:30:47Z
141345 marked the issue as duplicate of #962
#1 - c4-judge
2023-12-04T21:40:15Z
alex-ppg marked the issue as duplicate of #1323
#2 - c4-judge
2023-12-08T18:17:51Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: The_Kakers
Also found by: 00xSEV, 0xAsen, 0xDetermination, 0xJuda, 0xWaitress, 0xhunter, 0xlemon, 0xpiken, Al-Qa-qa, Arabadzhiev, CSL, CaeraDenoir, DarkTower, DeFiHackLabs, Greed, Haipls, MaNcHaSsS, NentoR, NoamYakov, PENGUN, Ruhum, Soul22, SovaSlava, Talfao, Toshii, TuringConsulting, VAD37, Vagner, Valix, Viktor_Cortess, ZdravkoHr, audityourcontracts, btk, codynhat, flacko, funkornaut, glcanvas, gumgumzum, immeas, innertia, ke1caM, lanrebayode77, lsaudit, mrudenko, niki, nmirchev8, openwide, oualidpro, r0ck3tz, rvierdiiev, trachev, yojeff
2.7688 USDC - $2.77
In the AuctionDemo.sol
, the claimAuction
function has a big flaw that allows any losing bidder to trigger any code without a gas limit. This could make it possible to an attacker to poison the entire auction, locking the funds of everyone there.
In the claimAuction
function, when the winner or an admin calls it, it is coded to send the funds back to every bidder that lost the auction via a call, but it does not limit the amount of gas forwarded, thus allowing a bad a actor to consume a huge amount of gas without reverting the transaction on his call, but using almost all the gas allowed, thus making the function revert when it tries to use more gas later.
Since claimAuction
is the only way to refund the lost bids and also the only way for the winner to claim the nft, this would result in a loss of the entire funds of the auction.
I'd consider it's impact to be high since an attacker can maliciously prevent everyone from getting a refund and lock the entire ether existing in the contract forever once the auctionEndTime
timestamp is surpassed.
A new auction is created, attacker poisons the contract with a 1 gwei bid.
Auction current winner: Attacker Contract balance: 1 gwei
Alice enter the bid with 1 ether:
Auction current winner: Alice Contract balance: 1 ether + 1 gwei
time passes and claimAuction
becomes available to be called, while cancelBid
and cancelAllBids
become unable to be called.
Alice, who won the auction, calls claimAuction
:
Auction winner: Alice Contract balance: 1 ether + 1 gwei
The claimAuction
function sends the ether to the attacker first.
This triggers the attacker receive
function, which is coded to use the maximum amount of gas that is allowed to use without reverting.
claimAuction
then tries to continue with the transfer of the nft, but it fails as there is no more gas to be used.
This makes the claimAuction
revert.
Auction current winner: Alice Contract balance: 1 ether + 1 gwei
The funds end up locked and the NFT ends up not being transfered.
VSCode
Limit the gas that every call is allowed to spend to prevent this kind of gas related lock of funds + DoS.
DoS
#0 - c4-pre-sort
2023-11-20T02:41:46Z
141345 marked the issue as duplicate of #486
#1 - c4-judge
2023-12-01T22:54:31Z
alex-ppg marked the issue as not a duplicate
#2 - c4-judge
2023-12-01T22:54:44Z
alex-ppg marked the issue as duplicate of #1782
#3 - c4-judge
2023-12-08T21:02:28Z
alex-ppg marked the issue as satisfactory
🌟 Selected for report: ast3ros
Also found by: 00xSEV, Al-Qa-qa, CaeraDenoir, Jiamin, Juntao, Ruhum, bart1e, circlelooper, crunch, gumgumzum, rishabh, smiling_heretic, ustas
95.7343 USDC - $95.73
Medium impact, it affects the functionallity of the nfts with other contracts, as they wouldn't want to interact with tokens that could get burned.
in the NextGenCore
contract, the burnToMint
function has the minting of the new nft, which makes an external call via _safeMint
, before the burning of the nft-to-burn. This allows any bad actor to transfer the nft to a victim contract without the victim contract being able to stop the _burn
from happening.
Make the mint of the nft in burnToMint
happen after the burning of the nft that gives access to the mint.
Token-Transfer
#0 - c4-pre-sort
2023-11-20T02:41:20Z
141345 marked the issue as duplicate of #1198
#1 - c4-pre-sort
2023-11-20T09:25:09Z
141345 marked the issue as duplicate of #1597
#2 - c4-pre-sort
2023-11-26T14:00:23Z
141345 marked the issue as duplicate of #1742
#3 - c4-judge
2023-11-29T19:53:28Z
alex-ppg marked the issue as not a duplicate
#4 - c4-judge
2023-11-29T19:53:44Z
alex-ppg marked the issue as duplicate of #1597
#5 - c4-judge
2023-12-08T21:23:50Z
alex-ppg marked the issue as partial-50