TSender

Cyfrin
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Theft of user funds in ZkSync due to `msg.sender` preservation in cross-chain calls

Summary

The TSender.sol contract, designed for ERC20 airdrops, is intended to be deployed on different blockchains, including Ethereum and ZkSync. However, due to the preservation of msg.sender in ZkSync for L1 -> L2 calls, a high-risk vulnerability exists. This vulnerability allows attackers to impersonate other users' contract-based accounts holding ERC20 tokens on ZkSync by deploying a contract with the same address on Ethereum. This report outlines the vulnerability details, impact, and provides recommendations to mitigate the risk.

Vulnerability Details

Description

For the ZkSync chain, msg.sender is preserved for L1 -> L2 calls. One of the rules when pursuing a cross-chain strategy is to never assume that address control between L1 and L2 is always guaranteed. For EOAs (i.e., non-contract accounts), it is generally true that any account accessible on Ethereum will also be accessible on other EVM-based chains. However, this is not always true for contract-based accounts, as the same account/wallet address might be owned by different persons on different chains. This can happen for example if there is a poorly implemented smart contract wallet factory on multiple EVM-based chains that deterministically deploys a wallet based on user-defined inputs.

For instance, if a smart contract wallet factory deployed on both EVM-based chains uses deterministic CREATE2, which allows users to define its salt when deploying the wallet, Bob might use ABC as salt on Ethereum, and Alice might use ABC as salt on ZkSync. Both of them will end up with the same wallet address on two different chains. A similar issue occurred in the Optimism-Wintermute Hack, but the actual incident is more complicated.

Bob (the attacker) saw that the 0xABC address is not owned by anyone on Ethereum. Therefore, he proceeds to take ownership of the 0xABC by interacting with the wallet factory to deploy a smart contract wallet at the same address on Ethereum. Bob can do so by checking the inputs that Alice used to create the wallet previously. Thus, Bob can technically make a request from L1 -> L2 to impersonate Alice's contract.

Exploit Scenario

  1. An attacker sees that there are wallet contracts in ZkSync with ERC20 tokens that have given spending approval to TSender.sol.

  2. Impersonation Attack: The attacker can deploy a contract on Ethereum with the same address as a contract owned by a victim on ZkSync. The attacker can then perform an L1 -> L2 call to impersonate the victim’s contract on ZkSync.

  3. Unauthorized Access: This impersonation can bypass access controls based on msg.sender, allowing the attacker to invoke airdropERC20() from his malicious contract on Ethereum. The airdropERC20() function will believe it was invoked by a legitimate contract in ZkSync and will perform the token transfers from this contract to the attacker-provided address[] recipients, eventually allowing the attacker to steal all the funds from the victim's wallet contract.

function airdropERC20(
address tokenAddress,
address[] calldata recipients,
uint256[] calldata amounts,
uint256 totalAmount
) external {
assembly {
....
// transferFrom(address from, address to, uint256 amount)
// cast sig "transferFrom(address,address,uint256)"
// This will result in memory looking like this:
// 0x00: 0x23b872dd00000000000000000000000000000000000000000000000000000000
mstore(0x00, hex"23b872dd")
// from address
mstore(0x04, caller())
// to address (this contract)
mstore(0x24, address())
// total amount
mstore(0x44, totalAmount)
if iszero(call(gas(), tokenAddress, 0, 0x00, 0x64, 0, 0)) {
mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
revert(0x1c, 0x04)
}
More code here...
// transfer the tokens
if iszero(call(gas(), tokenAddress, 0, 0x00, 0x44, 0, 0)) {
mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
revert(0x1c, 0x04)
}
...
}

This issue is only specific to ZkSync chain due to the preservation of msg.sender for L1 -> L2 calls. For the other
chains, the msg.sender is not preserved for L1 -> L2 calls and will always point to the L2's AMB forwarding the
requests.

Impact

Theft of user funds.

Tools Used

Manual code review

Recommendations

Restrict Access to EOAs: Modify the contract to allow only externally owned accounts to interact with the airdropERC20() function on ZkSync.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
0xsecuri Submitter
about 1 year ago
patrickalphac Auditor
about 1 year ago
0xsecuri Submitter
about 1 year ago
patrickalphac Auditor
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.