
SharkTeam: Analysis of the Principle of Multicall Arbitrary Address Spoofing Vulnerability
TechFlow Selected TechFlow Selected

SharkTeam: Analysis of the Principle of Multicall Arbitrary Address Spoofing Vulnerability
The root cause of this attack: in ERC-2771, the [Forwarder] is not specifically designed for multicall.
Author: SharkTeam
On December 8, 2023, OpenZeppelin officially issued an important security alert to the community. The alert indicated that when integrating ERC-2771 standard with multicall-like patterns in projects, there may be a risk of arbitrary address spoofing attacks.
SharkTeam conducted immediate technical analysis on this incident and summarized preventive measures, hoping future projects can learn from this case and jointly strengthen the security defenses of the blockchain industry.
1. Attack Transaction Analysis
Due to a series of attack transactions related to this vulnerability, we selected one representative transaction for analysis.
Attacker address:
0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A
Attack transaction:
0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6
Attack process:
1. First, the attacker (0xFDe0d157) used 5 WETH to swap approximately 3,455,399,346 TIME tokens.

2. Subsequently, the attacker (0xFDe0d157) constructed malicious calldata parameters and called the [Forwarder].execute function.
3. When calling [Forwarder].execute, the malicious calldata triggered the multicall function within the TIME contract. Then, using the remaining calldata, it executed the burn function of the TIME contract, burning TIME tokens in the pool.

2. Vulnerability Analysis
This attack primarily involves several aspects: ERC2771, Multicall, and carefully crafted calldata. We can identify relevant inheritance from the TIME token contract:

1. ERC2771 provides the ability to have a virtual msg.sender, allowing users to delegate third-party [Forwarder] contracts to execute transactions, thereby reducing gas costs. During transaction submission, the msg.sender address is appended to the calldata.
2. The TIME token contract inherits from ERC2771Context. When the [Forwarder] calls the contract, _msgSender() checks the calldata and right-shifts it, truncating the last 20 bytes as the expected msg.sender.

3. Multicall is a method that transforms a single function call into sequential execution of multiple functions within the same contract. It accepts an array of encoded user calls and executes them on its own contract. This function iterates through the call array and performs delegatecall() on each operation. This allows users to combine a series of operations and execute them sequentially within a single transaction without predefined combinations in the protocol, primarily aiming to save gas.

4. Regarding the carefully constructed calldata, the attacker called the [Forwarder].execute function and passed in relevant parameters.

After formatting the data value into readable form:

The attacker (0xFDe0d157) obtained a new data value by manipulating the offset of the current calldata and passed this value to the multicall(bytes[]) function. The first 4 bytes of the new data represent the selector of the burn(uint256) function, with the amount parameter set to 62227259510000000000000000000.
5. Within the multicall(bytes[]) function, the burn(uint256) function is invoked via delegatecall. In row 0x20, the address 0x760dc1e043d99394a10605b2fa08f123d60faf84 was initially appended at the end during calldata construction. This address corresponds to the TIME-ETH liquidity pool on Uniswap V2, which is the expected msg.sender mentioned earlier.

6. Why did the previously mentioned msg.sender become the TIME-ETH liquidity pool address? Initially, msg.sender was the [Forwarder] contract address. To verify if it's a trusted [Forwarder], if so, msg.sender is set to the last 20 bytes of the calldata.
3. Security Recommendations
The root cause of this attack: In ERC-2771, the [Forwarder] is not specifically designed for multicall. Attackers added parameters related to _msgSender() into external calls of multicall—specifically, the [Forwarder].execute function in this event. Inside the multicall function, some functions also append parameters from _msgSender(), enabling attackers to spoof _msgSender(). Therefore, by using multicall to invoke relevant functions, attackers can impersonate calls from any address. Ultimately, they authorized the burning of TIME tokens in the pool.
For this issue, the following mitigation and prevention measures can be taken:
1. Use the updated version with bug fixes. OpenZeppelin's new version of Multicall includes a context suffix length for ERC2771context data, indicating the expected suffix length for ERC-2771 contexts. Thus, any call from a trusted [Forwarder] will be correctly identified and adapted for each sub-function call.

Below is a comparison between the buggy version and the updated version:

2. Prohibit any contract from calling multicall to prevent [Forwarder] from using it. Taking ThirdWeb as an example, this approach differs from OpenZeppelin’s solution, where OpenZeppelin still allows multicall through contracts. Below is a comparison of ThirdWeb's buggy and updated versions.

[Disclaimer] The market entails risks; investment should be approached with caution. This article does not constitute investment advice. Readers should consider whether any opinions, viewpoints, or conclusions presented herein are suitable for their specific circumstances. Investment decisions made based on this information are at the reader's own risk.
Join TechFlow official community to stay tuned
Telegram:https://t.me/TechFlowDaily
X (Twitter):https://x.com/TechFlowPost
X (Twitter) EN:https://x.com/BlockFlow_News










