code-423n4 / 2023-11-zetachain-findings

0 stars 0 forks source link

ZetaConnectorZEVM#send will always revert because OutOfGas #231

Closed c4-bot-10 closed 11 months ago

c4-bot-10 commented 12 months ago

Lines of code

https://github.com/code-423n4/2023-11-zetachain/blob/b237708ed5e86f12c4bddabddfd42f001e81941a/repos/protocol-contracts/contracts/zevm/ZetaConnectorZEVM.sol#L84

Vulnerability details

Impact

ZetaConnectorZEVM#send will always revert because OutOfGas

Proof of Concept

WETH contract sends ETH via the transfer method when withdraw is called. This presents an issue because transfer only forwards 2300 gas, this however is not enough for the require check in the receive function, therefore it always reverts.

From the WETH contract:

    function withdraw(uint wad) public {
        require(balanceOf[msg.sender] >= wad);
        balanceOf[msg.sender] -= wad;
        msg.sender.transfer(wad);
        Withdrawal(msg.sender, wad);
    }

ZetaConnectorZEVM:

    receive() external payable {
        if (msg.sender != wzeta) revert OnlyWZETA();
    }

    function send(ZetaInterfaces.SendInput calldata input) external {
        // Transfer wzeta to "fungible" module, which will be burnt by the protocol post processing via hooks.
        if (!WZETA(wzeta).transferFrom(msg.sender, address(this), input.zetaValueAndGas)) revert WZETATransferFailed();
@>      WZETA(wzeta).withdraw(input.zetaValueAndGas);
        (bool sent, ) = FUNGIBLE_MODULE_ADDRESS.call{value: input.zetaValueAndGas}("");
        if (!sent) revert FailedZetaSent();
        emit ZetaSent(
            tx.origin,
            msg.sender,
            input.destinationChainId,
            input.destinationAddress,
            input.zetaValueAndGas,
            input.destinationGasLimit,
            input.message,
            input.zetaParams
        );
    }

Here is a demo contract in foundry to demo this

contract Counter {
     address public immutable funder = 0x4F4495243837681061C4743b74B3eEdf548D56A5 ;
     IWETH9 immutable weth = IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
    function unwrap() public {
        weth.withdraw(100 ether);
    }
    receive() external payable{
      require(funder == msg.sender);
    }
}

The Foundry test :

contract CounterTest is Test{
Counter attaxk;
address funder = 0x4F4495243837681061C4743b74B3eEdf548D56A5;
IWETH9 weth =  IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
uint256 forkId = vm.createSelectFork("https://eth.llamarpc.com");

   function setUp() public{
      attaxk = new Counter();
      console.log(weth.balanceOf(address(attaxk)));
   vm.startPrank(funder);
   weth.transfer(address(attaxk), 101 ether);

   console.log(weth.balanceOf(address(attaxk)));
   }

   function testaa() public{
     attaxk.unwrap();
     console.log(address(attaxk).balance);
   }

}

Tools Used

vscode manual

Recommended Mitigation Steps

Remove require checks from inside the receive() function

Assessed type

Error

c4-bot-6 commented 11 months ago

Withdrawn by zhaojie