SharkTeam:ERC2771&Multicall任意地址欺骗漏洞原理分析
字数 1691 2025-08-10 08:28:18

ERC2771与Multicall任意地址欺骗漏洞分析及防范指南

漏洞概述

2023年12月8日,OpenZeppelin发布安全警报,指出在项目集成中使用ERC-2771标准与类Multicall方式时,存在任意地址欺骗攻击的风险。该漏洞允许攻击者通过精心构造的calldata参数,欺骗合约的_msgSender()函数,模仿任意地址进行调用。

攻击案例分析

攻击交易详情

  • 攻击者地址: 0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A
  • 攻击交易哈希: 0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6

攻击流程

  1. 攻击者先用5枚WETH兑换了约3,455,399,346枚TIME代币
  2. 构建恶意calldata参数并调用[Forwarder].execute函数
  3. 恶意calldata触发TIME合约的multicall函数
  4. 利用剩余calldata触发执行TIME合约的burn函数,销毁池中的TIME代币

技术原理分析

涉及的关键技术

1. ERC2771标准

  • 提供虚拟msg.sender能力,允许用户委托第三方[Forwarder]执行交易
  • 提交交易时,msg.sender地址会被添加到calldata中
  • 合约继承ERC2771Context后,_msgSender()会检查calldata数据,截取最后20字节作为预期的msg.sender

2. Multicall功能

  • 将单个函数调用转变为在同一个合约中按顺序调用多个函数的方法
  • 接受用户编码调用的数组并对其自身合约执行
  • 遍历调用数组,对每个操作执行delegatecall()
  • 主要目的是节省gas费用

3. 精心构造的calldata

攻击者通过以下方式构造恶意数据:

  1. 调用[Forwarder].execute函数并传入特定参数
  2. 通过对当前calldata的偏移操作获得新的data值
  3. 新data的前4个字节是burn(uint256)函数的选择器
  4. multicall(bytes[])函数中通过delegatecall调用burn(uint256)函数
  5. 将目标地址(如Uniswap v2上的TIME-ETH流动性池地址)添加在calldata末尾

漏洞根本原因

  1. ERC-2771的[Forwarder]并非专为multicall设计
  2. 攻击者将_msgSender()函数中的相关参数添加到multicall的外部调用中
  3. 在multicall函数中,某些函数会附加_msgSender()中的参数
  4. 导致攻击者可以欺骗_msgSender(),模仿任意地址调用
  5. 最终通过授权销毁池子里的代币

安全建议与修复方案

OpenZeppelin官方修复方案

  1. 新版本Multicall带有ERC2771context数据的context后缀长度标识
  2. 来自可信任[Forwarder]的任何call都将被正确识别并适应每个子函数call
  3. 仍然允许通过合约进行multicall

ThirdWeb的修复方案

  1. 禁止任何合约调用multicall
  2. 防止[Forwarder]使用multicall功能
  3. 相比OpenZeppelin的方案更为严格

最佳实践建议

  1. 及时升级:使用OpenZeppelin或ThirdWeb修复后的最新版本
  2. 权限控制:严格限制multicall函数的访问权限
  3. 输入验证:对所有传入的calldata进行严格验证
  4. 事件监控:监控合约中的异常调用模式
  5. 安全审计:在集成ERC2771和Multicall功能前进行专业安全审计

总结

ERC2771与Multicall的组合漏洞展示了智能合约开发中"功能组合"可能带来的安全隐患。开发者在集成多个功能模块时,需要特别注意它们之间的交互可能产生的副作用。通过理解此漏洞的原理和修复方案,开发者可以更好地保护自己的合约免受类似攻击。

ERC2771与Multicall任意地址欺骗漏洞分析及防范指南 漏洞概述 2023年12月8日,OpenZeppelin发布安全警报,指出在项目集成中使用ERC-2771标准与类Multicall方式时,存在任意地址欺骗攻击的风险。该漏洞允许攻击者通过精心构造的calldata参数,欺骗合约的 _msgSender() 函数,模仿任意地址进行调用。 攻击案例分析 攻击交易详情 攻击者地址 : 0xFDe0d1575Ed8E06FBf36256bcdfA1F359281455A 攻击交易哈希 : 0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6 攻击流程 攻击者先用5枚WETH兑换了约3,455,399,346枚TIME代币 构建恶意calldata参数并调用 [Forwarder].execute 函数 恶意calldata触发TIME合约的 multicall 函数 利用剩余calldata触发执行TIME合约的 burn 函数,销毁池中的TIME代币 技术原理分析 涉及的关键技术 1. ERC2771标准 提供虚拟 msg.sender 能力,允许用户委托第三方[ Forwarder ]执行交易 提交交易时, msg.sender 地址会被添加到calldata中 合约继承 ERC2771Context 后, _msgSender() 会检查calldata数据,截取最后20字节作为预期的 msg.sender 2. Multicall功能 将单个函数调用转变为在同一个合约中按顺序调用多个函数的方法 接受用户编码调用的数组并对其自身合约执行 遍历调用数组,对每个操作执行 delegatecall() 主要目的是节省gas费用 3. 精心构造的calldata 攻击者通过以下方式构造恶意数据: 调用 [Forwarder].execute 函数并传入特定参数 通过对当前calldata的偏移操作获得新的data值 新data的前4个字节是 burn(uint256) 函数的选择器 在 multicall(bytes[]) 函数中通过 delegatecall 调用 burn(uint256) 函数 将目标地址(如Uniswap v2上的TIME-ETH流动性池地址)添加在calldata末尾 漏洞根本原因 ERC-2771的[ Forwarder ]并非专为multicall设计 攻击者将 _msgSender() 函数中的相关参数添加到multicall的外部调用中 在multicall函数中,某些函数会附加 _msgSender() 中的参数 导致攻击者可以欺骗 _msgSender() ,模仿任意地址调用 最终通过授权销毁池子里的代币 安全建议与修复方案 OpenZeppelin官方修复方案 新版本Multicall带有 ERC2771context 数据的context后缀长度标识 来自可信任[ Forwarder ]的任何call都将被正确识别并适应每个子函数call 仍然允许通过合约进行multicall ThirdWeb的修复方案 禁止任何合约调用multicall 防止[ Forwarder ]使用multicall功能 相比OpenZeppelin的方案更为严格 最佳实践建议 及时升级 :使用OpenZeppelin或ThirdWeb修复后的最新版本 权限控制 :严格限制multicall函数的访问权限 输入验证 :对所有传入的calldata进行严格验证 事件监控 :监控合约中的异常调用模式 安全审计 :在集成ERC2771和Multicall功能前进行专业安全审计 总结 ERC2771与Multicall的组合漏洞展示了智能合约开发中"功能组合"可能带来的安全隐患。开发者在集成多个功能模块时,需要特别注意它们之间的交互可能产生的副作用。通过理解此漏洞的原理和修复方案,开发者可以更好地保护自己的合约免受类似攻击。