The order of state changes and external calls in the function Distribution._withdraw
may introduce a reentrancy vulnerability.
The order of state changes and external calls in the function Distribution._withdraw
may introduce a reentrancy vulnerability. External calls should generally be placed at the end of the function to prevent potential reentrancy attacks.
Found in contracts/Distribution.sol:
User and/or pool deposit may be drained.
Hardhat
Proof of Code (POC)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract ReentrancyVulnerableToken is IERC20 {
mapping(address => uint256) private balances;
function transfer(address to, uint256 amount) external override returns (bool) {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
return true;
}
function balanceOf(address account) external view override returns (uint256) {
return balances[account];
}
// Simplified for demonstration purposes
function safeTransfer(address to, uint256 amount) external {
// Assume external call here
(bool success, ) = to.call{value: 0, gas: gasleft()}("");
require(success, "External call failed");
}
}
contract ReentrancyVulnerableContract {
IERC20 public token;
constructor(address tokenAddress) {
token = IERC20(tokenAddress);
}
function maliciousWithdraw() external {
// Malicious token contract reenters this contract during the transfer
token.safeTransfer(address(this), 1);
}
function withdrawAndFail() external {
uint256 balanceBefore = token.balanceOf(address(this));
// Vulnerable code where external call is before state changes
token.safeTransfer(msg.sender, 1);
// Incorrectly expecting the balance to be zero
require(token.balanceOf(address(this)) == 0, "Balance not updated");
}
}
To reduce the risk of reentrancy attacks, the line IERC20(depositToken).safeTransfer(user_, amount_); should be moved outside of the if (pool.isPublic) block and placed after the state changes.
By moving the safeTransfer call outside of the if (pool.isPublic) block, you ensure that the state changes are already completed before any external interaction takes place. This helps in minimizing the risk of reentrancy attacks.
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.