Closed c4-submissions closed 1 year ago
0xA5DF marked the issue as duplicate of #887
0xA5DF marked the issue as sufficient quality report
alcueca changed the severity to 2 (Med Risk)
alcueca marked the issue as satisfactory
alcueca marked the issue as duplicate of #518
Lines of code
https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L779 https://github.com/code-423n4/2023-09-maia/blob/main/src/RootBridgeAgent.sol#L938-L948 https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/MulticallRootRouter.sol#L312-L331 https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/src/RootBridgeAgentExecutor.sol#L159-L191
Vulnerability details
Impact
The MulticallRootRouter and MulticallRootRouterLibZip contracts can receive ETH and there is no way to take out the ETH in this contract, so ETH can be stuck in these contracts.
In some flow, this contract receive ETH but don't send the ETH to other contracts, so it can accumulate the ETH. But there is no way to take out the ETH in this contract.
For example, let examine the flow of one scenario:
On the Branch Bridge Agent, user call
The message with payload[0] = 0x85 (if _hasFallbackToggled is true) or 0x05 will be sent to the Root Bridge Agent. Then in Root Bridge Agent, in the lzReceive Root Bridge Agent
Then the executeSignedWithDeposit of bridgeAgentExecutorAddress is called. Notice that value is address(this).balance of the Root Bridge Agent => So the bridgeAgentExecutorAddress will receive the ETH from the Root Bridge Agent.
Note: The ETH in Root Bridge Agent and Branch Bridge Agent can come from the Layerzero endpoint when users uses adapter parameters: _gasParams.remoteBranchExecutionGas as airdrop native token via Layerzero as explained in (https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters). We can see this is similuted in the test cases of Maia: https://github.com/code-423n4/2023-09-maia/blob/c0dc3550e0754571b82d7bfd8f0282ac8fa5e42f/test/ulysses-omnichain/MulticallRootRouterTest.t.sol#L1384-L1386
But notice that in the lzReceive of Root Bridge Agent and Branch Bridge Agent, it is used excessivelySafeCall to lzReceiveNonBlocking, so if the call to lzReceiveNonBlocking is failed (reverted), the message delivery from the Layerzero endpoint is still succesfull. So for some transactions that the call to lzReceiveNonBlocking is reverted, it does not spend ETH, but the contract still received ETH from Layerzero, so ETH is accumulated in Root Bridge Agent and Branch Bridge Agent. This ETH can come from different users with different transactions.
End of Note.
bridgeAgentExecutorAddress
Then in Root Bridge Executor executeSignedWithDeposit
Notice that ETH amnount of value = msg.value is transferred to Router. In the case of MulticallRootRouter Title
Notice that with funcId == 0x01, the call to the virtual account call function do not spend the ETH. So the amount of ETH is kept in Multicall Root Router.
In the case, users use the DepositMultiple, the issue and the flow is similar.
For the function executeSignedDepositMultiple in MulticallRootRouter, with functionID = 0x01 it is similar. Title
Proof of Concept
Step 1: Check the ETH balance of MulticallRootRouter. It is 0
Step 2: Users call on the Branch Bridge Agent or the Root Bridge Agent with the _params that the funcID = 0x01 in executeSignedDepositSingle
Step 3: Check ETH balance of MulticallRootRouter. It is not 0
for the POC, I have modified the testMulticallSignedNoOutputDepositSingle just to add the console2.log to log the ETH balance of the MulticallRootRouter before and after the transaction from LayerZero EndPoint to the Root Bridge Agent. Title
to run the test
The log: Title
You can see from the log that ETH balance of rootMulticallRouter 500000000000000000
Tools Used
Manual Review
Recommended Mitigation Steps
Should implement a function to take out the ETH stuck in this contract MulticallRootRouter and MulticallRootRouterLibZip
Assessed type
ETH-Transfer