ERC2771 Multicall任意地址欺骗攻击分析
字数 1655 2025-08-10 08:28:24

ERC2771 Multicall任意地址欺骗攻击分析教学文档

1. 漏洞概述

ERC2771与Multicall标准的不安全集成导致了一个严重的任意地址欺骗漏洞,攻击者可以利用该漏洞伪造交易发送者地址,从而未经授权地操作合约功能。该漏洞影响了多个ERC20代币合约,导致大量资金被盗。

2. 漏洞背景

2.1 相关标准介绍

ERC2771标准:用于实现原生元交易(Meta Transaction)的协议,允许用户使用签名而非ETH支付gas费用。关键特性是重写了_msgSender()函数,可以从calldata中提取实际交易发起者。

Multicall标准:允许在单个交易中批量执行多个函数调用,提高效率并减少gas成本。

2.2 漏洞产生原因

当合约同时实现ERC2771和Multicall标准时,攻击者可以构造特殊的calldata,通过Multicall的delegatecall机制绕过ERC2771的签名验证,伪造_msgSender()返回值。

3. 攻击案例分析

3.1 受影响代币

  • ETH主网:HXA、WFCA、TIME、NAME等
  • Polygon链:Swop等

3.2 典型攻击流程

以TIME代币为例:

  1. 攻击者部署MEV bot合约(攻击合约)
  2. 攻击合约用5ETH兑换WETH
  3. 用WETH兑换345,539,9346个TIME代币
  4. 构造恶意calldata调用代币合约的multicall函数
  5. multicall delegatecall执行代币的burn函数,燃烧池子地址中的62,227,259,510个TIME
  6. 代币价格因供应量减少而上涨
  7. 攻击者卖出TIME获利约94ETH

4. 技术原理分析

4.1 关键代码分析

Forward.sol中的execute函数

function execute(ForwardRequest calldata req, bytes calldata signature) public payable returns (bool, bytes memory) {
    require(verify(req, signature), "MinimalForwarder: signature does not match request");
    _nonces[req.from] = req.nonce + 1;
    
    (bool success, bytes memory result) = req.to.call{ gas: req.gas, value: req.value }(
        abi.encodePacked(req.data, req.from)
    );

攻击者构造的恶意calldata示例:

0xac9650d8000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003842966c680000000000000000000000000000000000000000c9112ec16d958e8da8180000760dc1e043d99394a10605b2fa08f123d60faf840000000000000000

MulticallUpgradeable.sol

function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
    results = new bytes[](data.length);
    for (uint256 i = 0; i < data.length; i++) {
        results[i] = _functionDelegateCall(address(this), data[i]);
    }
    return results;
}

function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
    require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
    (bool success, bytes memory returndata) = target.delegatecall(data);
    return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}

ERC2771的_msgSender()实现

function _msgSender() internal view returns (address payable signer) {
    signer = msg.sender;
    if (msg.data.length>=20 && isTrustedForwarder(signer)) {
        assembly {
            signer := shr(96,calldataload(sub(calldatasize(),20)))
        }
    }
}

4.2 漏洞利用原理

  1. 攻击者构造特殊的calldata,使Multicall执行delegatecall时,EVM根据偏移量截取特定部分作为函数参数
  2. 截取的部分包含burn函数签名和伪造的调用者地址(通常是流动性池地址)
  3. 由于delegatecall保留了原始调用的上下文,ERC2771的_msgSender()会从calldata末尾提取伪造的地址
  4. 合约误认为调用来自合法地址(如流动性池),执行了本应受限的操作(如燃烧代币)

5. 漏洞复现方法

5.1 复现环境

  1. 使用测试网或fork主网环境
  2. 部署攻击合约(参考已公开的攻击合约)
  3. 准备目标代币合约(需同时实现ERC2771和Multicall)

5.2 复现步骤

  1. 部署攻击合约(MEV bot)
  2. 准备初始资金(如兑换WETH)
  3. 购买目标代币
  4. 构造恶意calldata调用multicall函数
  5. 观察代币燃烧效果
  6. 出售代币获利

6. 防御措施

6.1 合约开发者

  1. 避免同时使用ERC2771和Multicall:两种标准的安全模型存在根本冲突
  2. 使用OpenZeppelin的修复方案:他们发布了专门解决此问题的补丁
  3. 实现严格的权限控制:即使_msgSender()被伪造,关键操作仍需多重验证
  4. 使用msg.sender而非_msgSender():对于敏感操作,直接使用原始发送者

6.2 项目方应急方案

  1. 暂停受影响合约
  2. 升级合约实现
  3. 迁移资金到安全合约
  4. 通知交易所暂停交易受影响代币

7. 相关资源

  1. ERC-2771标准文档
  2. OpenZeppelin安全公告
  3. ThirdWeb安全报告
  4. 攻击交易示例:

8. 总结

ERC2771与Multicall的组合漏洞展示了标准集成时可能产生的意外安全问题。此事件强调了:

  1. 标准组合需谨慎评估安全影响
  2. 元交易实现需要特别关注身份验证
  3. 合约升级需全面测试兼容性
  4. 安全监控和应急响应机制的重要性
ERC2771 Multicall任意地址欺骗攻击分析教学文档 1. 漏洞概述 ERC2771与Multicall标准的不安全集成导致了一个严重的任意地址欺骗漏洞,攻击者可以利用该漏洞伪造交易发送者地址,从而未经授权地操作合约功能。该漏洞影响了多个ERC20代币合约,导致大量资金被盗。 2. 漏洞背景 2.1 相关标准介绍 ERC2771标准 :用于实现原生元交易(Meta Transaction)的协议,允许用户使用签名而非ETH支付gas费用。关键特性是重写了 _msgSender() 函数,可以从calldata中提取实际交易发起者。 Multicall标准 :允许在单个交易中批量执行多个函数调用,提高效率并减少gas成本。 2.2 漏洞产生原因 当合约同时实现ERC2771和Multicall标准时,攻击者可以构造特殊的calldata,通过Multicall的delegatecall机制绕过ERC2771的签名验证,伪造 _msgSender() 返回值。 3. 攻击案例分析 3.1 受影响代币 ETH主网:HXA、WFCA、TIME、NAME等 Polygon链:Swop等 3.2 典型攻击流程 以TIME代币为例: 攻击者部署MEV bot合约(攻击合约) 攻击合约用5ETH兑换WETH 用WETH兑换345,539,9346个TIME代币 构造恶意calldata调用代币合约的multicall函数 multicall delegatecall执行代币的burn函数,燃烧池子地址中的62,227,259,510个TIME 代币价格因供应量减少而上涨 攻击者卖出TIME获利约94ETH 4. 技术原理分析 4.1 关键代码分析 Forward.sol中的execute函数 : 攻击者构造的恶意calldata示例: MulticallUpgradeable.sol : ERC2771的_ msgSender()实现 : 4.2 漏洞利用原理 攻击者构造特殊的calldata,使Multicall执行delegatecall时,EVM根据偏移量截取特定部分作为函数参数 截取的部分包含burn函数签名和伪造的调用者地址(通常是流动性池地址) 由于delegatecall保留了原始调用的上下文,ERC2771的_ msgSender()会从calldata末尾提取伪造的地址 合约误认为调用来自合法地址(如流动性池),执行了本应受限的操作(如燃烧代币) 5. 漏洞复现方法 5.1 复现环境 使用测试网或fork主网环境 部署攻击合约(参考已公开的攻击合约) 准备目标代币合约(需同时实现ERC2771和Multicall) 5.2 复现步骤 部署攻击合约(MEV bot) 准备初始资金(如兑换WETH) 购买目标代币 构造恶意calldata调用multicall函数 观察代币燃烧效果 出售代币获利 6. 防御措施 6.1 合约开发者 避免同时使用ERC2771和Multicall :两种标准的安全模型存在根本冲突 使用OpenZeppelin的修复方案 :他们发布了专门解决此问题的补丁 实现严格的权限控制 :即使_ msgSender()被伪造,关键操作仍需多重验证 使用msg.sender而非_ msgSender() :对于敏感操作,直接使用原始发送者 6.2 项目方应急方案 暂停受影响合约 升级合约实现 迁移资金到安全合约 通知交易所暂停交易受影响代币 7. 相关资源 ERC-2771标准文档 OpenZeppelin安全公告 ThirdWeb安全报告 攻击交易示例: ETH主网攻击交易 Polygon攻击交易 8. 总结 ERC2771与Multicall的组合漏洞展示了标准集成时可能产生的意外安全问题。此事件强调了: 标准组合需谨慎评估安全影响 元交易实现需要特别关注身份验证 合约升级需全面测试兼容性 安全监控和应急响应机制的重要性