Open c4-submissions opened 11 months ago
bytes032 marked the issue as duplicate of #827
GalloDaSballo changed the severity to QA (Quality Assurance)
This previously downgraded issue has been upgraded by GalloDaSballo
GalloDaSballo marked the issue as satisfactory
GalloDaSballo changed the severity to QA (Quality Assurance)
Lines of code
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1ERC20Bridge.sol#L194-L197 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/bridge/L1WethBridge.sol#L181-L184 https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L309-L314
Vulnerability details
Impact
If either a contract or a multisig uses the legacy deposit in any of the bridges or calls
Mailbox, function requestL2Transaction
with_refundRecipient == address(0)
, then the destination refund recipient will be its aliased address. As the address derivation is different in zkSync Era, then, for the aliased address to be user-controlled, it would need to find the preimage of such address or find a collision, which is not feasible, so the contract may be losing therefundGas
or the whole bridged ETH if the transaction did fail on zkSync Era.Proof of concept
Let's start with the bridges (I will work with the ERC20 one, the WETH one is the same). If the caller tries to make a legacy deposit
L1ERC20Bridge, lines 144 to 152
it will set the
_refundRecipient
to 0 (the last argument) and the "real"deposit
function will doL1ERC20Bridge, lines 194 to 197
That means, if our caller is a contract or a multisig,
refundRecipient
will be set to the alias of the caller's address. The same happens if he tries bridging ETH directly throughMailbox
:Mailbox, lines 309 to 314
The issue is that
refundRecipient
may not be controlled by the caller because, if that were true, then the contract caller should have found the preimage of its aliased address on zkSync Era taking into account that even the address derivation is different, so the same bytecode will have two completely different addresses on Ethereum and zkSync EraContractDeployer, lines 92 to 123
which means the odds of finding a collision are near (quasi-exactly) zero. This is bad as
bootloader
sends the excess of gas of the requested L1 ⇒ L2 transaction to such address and, if the transaction failed, the whole ETH (sub the operator pay):bootloader, lines 937 to 959
so contracts using the legacy deposit may be losing either the gas refund or their entire assets if the transaction failed on zkSync Era.
Recommended Mitigation steps
Upgrade both bridges and
Mailbox
to revert if such deposits are made.Assessed type
Other