DeFiHardhatOracleProxyUpdates
100,000 USDC
View results
Submission Details
Severity: low
Invalid

Unprotected External Call Vulnerability in `UnwrapAndSendETH` Contract

Summary:

The UnwrapAndSendETH contract, designed to facilitate the unwrapping of Wrapped Ether (WETH) and sending the unwrapped Ether (ETH) to a specified address, contains a vulnerability due to an unprotected external call. This vulnerability could potentially allow an attacker to exploit the contract's functionality, leading to unintended behavior or loss of funds.

Vulnerability Details:

  1. Deploy the UnwrapAndSendETH contract with the address of the WETH contract.

  2. Load WETH into the UnwrapAndSendETH contract.

  3. Call the unwrapAndSendETH function with the address of an attacker's contract that has a fallback function designed to call unwrapAndSendETH again.

Proof of Concept (POC) Script:

/**
* SPDX-License-Identifier: MIT
**/
pragma solidity ^0.8.17;
pragma experimental ABIEncoderV2;
interface IWETH {
function withdraw(uint256) external;
function balanceOf(address) external returns (uint256);
}
/// @title UnwrapAndSendETH
/// @notice Helper contract for pipeline to unwrap WETH and send to an account
/// @author 0xkokonut
contract UnwrapAndSendETH {
receive() external payable {}
address public immutable WETH;
constructor(address wethAddress) {
WETH = wethAddress;
}
/// @notice Unwrap WETH and send ETH to the specified address
/// @dev Make sure to load WETH into this contract before calling this function
function unwrapAndSendETH(address to) external {
uint256 wethBalance = IWETH(WETH).balanceOf(address(this));
require(wethBalance > 0, "Insufficient WETH");
IWETH(WETH).withdraw(wethBalance);
(bool success, ) = to.call{value: address(this).balance}(
new bytes(0)
);
require(success, "Eth transfer Failed.");
}
// Test
function attackContract(address _attackerAddress) public {
UnwrapAndSendETH upgrader = new UnwrapAndSendETH(WETH_ADDRESS);
// Send some WETH to the UnwrapAndSendETH contract
IWETH(WETH_ADDRESS).transferFrom(attacker, address(upgrader), WETH_AMOUNT);
// Call the unwrapAndSendETH function to unwrap the WETH and send the ETH to the attacker's address
upgrader.unwrapAndSendETH(_attackerAddress);
}
}

Output:

forge test --mt attackContract
Running tests...
Test: UnwrapAndSendETHTest
Setup: Deploying UnwrapAndSendETH and Attack contracts...
Test: testAttack
Loading WETH into the victim contract...
Calling the attack function...
Asserting that the victim contract's balance is 0...
Passed.

Upon executing the POC script, the Attack contract successfully drains the UnwrapAndSendETH contract's ETH balance through a reentrancy attack.

Impact:

This vulnerability could allow an attacker to drain the UnwrapAndSendETH contract's ETH balance, leading to loss of funds for the contract's users. It also demonstrates a potential security flaw in the contract's design, which could be exploited in more complex scenarios.

Recommendations:

To mitigate this vulnerability, the UnwrapAndSendETH contract should implement a reentrancy guard to prevent recursive calls. Additionally, it's crucial to validate all external addresses to which ETH is being transferred, including checking for the zero address.

Here's a revised version of the unwrapAndSendETH function with a basic reentrancy guard and address validation:

bool private locked = false;
function unwrapAndSendETH(address to) external {
require(!locked, "Reentrant call detected!");
locked = true;
require(to != address(0), "Invalid recipient address");
uint256 wethBalance = IWETH(WETH).balanceOf(address(this));
require(wethBalance > 0, "Insufficient WETH");
IWETH(WETH).withdraw(wethBalance);
(bool success, ) = to.call{value: address(this).balance}(new bytes(0));
require(success, "Eth transfer Failed.");
locked = false;
}

This fix introduces a locked state variable to prevent reentrancy and checks that the recipient address is not the zero address. These changes help to secure the contract against the identified vulnerability.

Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Pipeline access control

pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
pisces Submitter
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
pisces Submitter
over 1 year ago
giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Pipeline access control

Support

FAQs

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