A critical vulnerability in the StarkNet auction contract allows the highest bidder to withdraw their funds after receiving the NFT if they invoke the withdraw
function before the contract owner calls withdraw
. This enables the highest bidder to receive the NFT and reclaim their funds, resulting in no payment for the contract owner.
The highest bidder’s bid is stored both in the highest_bid
field and the bid_values
map. After the auction ends, the highest bidder can still invoke the withdraw
function because their bid is also stored in bid_values
. This allows the highest bidder to withdraw their funds after receiving the NFT by calling end
.
The contract owner calls end
to finalize the auction and transfer the NFT to the highest bidder.
The highest bidder receives the NFT.
The highest bidder then invokes withdraw
and reclaims their bid from bid_values
.
This results in the highest bidder having both the NFT and their bid, leaving the contract owner without the proceeds from the auction.
Double benefit for the highest bidder: The highest bidder can receive the NFT and reclaim their bid, which allows them to win the auction without paying.
Loss for the contract owner: The contract owner will not receive the highest bid, leading to financial loss as the auction's purpose is defeated.
High exploitability: The vulnerability is easily exploitable by any highest bidder, making it a high-severity issue.
Manual code review.
To fix this issue, it is essential to prevent the highest bidder from calling the withdraw
function after they have won the auction. A check must be added in the withdraw
function to ensure that the highest bidder cannot withdraw their bid after the auction has ended.
Check for the highest bidder: The line assert(caller != self.highest_bidder.read(), "Highest bidder cannot withdraw their bid")
ensures that the highest bidder cannot invoke withdraw
after winning the auction.
Reset bid after withdrawal: In the case of other participants, their bid is reset in bid_values
after they withdraw their funds, ensuring no repeated withdrawals.
The `withdraw` function doesn't reset the `bid_values` to 0 after the withdraw. That means the bidder can call multiple time the `withdraw` function and receive the whole balance of the protocol.
The `withdraw` function allows the participants to receive back the value of all their unsuccessful bids. The problem is that the winner of the auction will receive all bids including the `highest_bid` that should be paid to the NFT owner.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.