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
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:
ZetaConnectorZEVM:
Here is a demo contract in foundry to demo this
The Foundry test :
Tools Used
vscode manual
Recommended Mitigation Steps
Remove require checks from inside the receive() function
Assessed type
Error