!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析

攻击概要

2023年12月8日,OpenZeppelin官方发推向社区发出重要安全警报,称ERC-2771标准与类multicall方式(代码中存在delegatecall到自身合约,但calldata外部用户可控的逻辑)的集成使用,将导致使用这种模式的项目面临任意地址欺骗攻击的风险。

(https://twitter.com/openzeppelin/status/1732913331265036475?s=46&t=zDQbmeyWt2t9a8SGpOvbtw)

OpenZeppelin官方在其博客中给出了识别漏洞合约并缓解漏洞的方法,详情请阅读博客:https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure

!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析


详细分析

从OpenZeppelin的博客可以看到,目前已经存在数笔攻击交易,此处我们选取一笔交易进行详细分析。

1. 攻击中涉及的关键地址

攻击交易:https://etherscan.io/tx/0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6

攻击者地址:

0xfde0d1575ed8e06fbf36256bcdfa1f359281455a

攻击合约:

0x6980a47bee930a4584b09ee79ebe46484fbdbdd0

TIME漏洞合约

0x4b0e9a7da8bab813efae92a6651019b8bd6c0a29


2. 攻击流程分析

2.1. 准备阶段

  1. 攻击者授权Uniswap V2 Router合约使用其TIME代币。

  2. 攻击者将5个ether存入WETH,获得5个WETH。

2.2. 攻击实施

  1. 攻击者调用Uniswap V2: Router合约的swapExactTokensForTokensSupportingFeeOnTransferTokens函数,意图使用5个WETH去换TIME。此次交换在Uniswap V2: TIME 40池子进行交换,攻击者换出了3455399346.269046个TIME。

  2. 攻击者调用Forwarder的execute函数,在参数req中指定to地址为TIME合约地址以及data。另一个参数signature是0xa16a5f37774309710711a8b4e83b068306b21724(攻击者另一个地址)这个地址做的签名,req.from也是这个地址,所以execute函数的签名校验可以顺利通过。接下来会去调用TIME合约的函数,传入的calldata为req.data以及req.from拼接的数据,值得注意的是,此时的calldata是攻击者精心构造的,里面包含了Uniswap V2: TIME 40池子的地址,而这个地址,正是后续攻击中损失资金的地址。

    struct ForwardRequest {    address from;    address to;    uint256 value;    uint256 gas;    uint256 nonce;    bytes data;}
    function execute(ForwardRequest calldata req, bytes calldata signature)publicpayablereturns (bool, bytes memory){ require(verify(req, signature), "MinimalForwarder: signature does not match request"); _nonces[req.from] = req.nonce + 1;
    // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory result) = req.to.call{ gas: req.gas, value: req.value }( abi.encodePacked(req.data, req.from) );
    if (!success) { // Next 5 lines from https://ethereum.stackexchange.com/a/83577 if (result.length < 68) revert("Transaction reverted silently"); assembly { result := add(result, 0x04) } revert(abi.decode(result, (string))); } // Check gas: https://ronan.eth.link/blog/ethereum-gas-dangers/ assert(gasleft() > req.gas / 63); return (success, result);}
  3. 观察交易得知,此时调用的是TIME合约的multicall函数。在multicall函数中,会通过delegatecall的方式调用到当前合约(TIME)合约的某个函数。本笔交易中,此处是调用到了TIME合约的burn函数。

    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");
    // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");}
  4. 调用到burn函数时,会通过_msgSender函数得出销毁代币的地址。进入到_msgSender函数,此时的msg.sender是Forwarder合约,该Forwarder合约是TIME合约信任的forwarder,因此isTrustedForwarder为true,会从calldata中最后20个字节取出一个地址作为sender。此时calldata如下所示。calldata最后20个字节对应的正好是Uniswap V2: TIME 40池子的地址。最终Uniswap V2: TIME 40池子的TIME代币被burn掉了62227259510个。

    function burn(uint256 amount) public virtual {    _burn(_msgSender(), amount);}
    function _msgSender() internal view virtual override returns (address sender) {    if (isTrustedForwarder(msg.sender)) {        // The assembly code is more direct than the Solidity version using `abi.decode`.        assembly {            sender := shr(96, calldataload(sub(calldatasize(), 20)))        }    } else {        return super._msgSender();    }}
    0x42966c68c9112ec16d958e8da8180000760dc1e043d99394a10605b2fa08f123d60faf84
  5. 攻击者调用Uniswap V2: TIME 40池子的sync函数,将reserve量更新为实际池子中的代币数量。更新完以后,池子中TIME代币的reserve量变少,价格也变贵了。

  6. 攻击者再次在Uniswap V2: TIME 40池子中进行交换,这一次是用TIME代币换WETH。因为TIME代币价格变贵,因此攻击者可以换出更多的WETH。最终攻击者使用第一交换得到的3455399346.269046个TIME换出了约94个WETH,赚取了80多个ETH。


建议

建议各个项目方赶紧通过OpenZeppelin的工具(https://defender.openzeppelin.com/v2/#/auth/sign-up)检查合约是否存在上述提到的漏洞,并立即采取暂停项目、取消信任的forwarder等措施,规避该风险。

!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
END



关于ZAN


ZAN依托于AntChain Open Labs的TrustBase开源开放技术体系,拥有Web3领域独特的优势和创新能力,为Web3社区提供可靠、高性价比的区块链应用开发技术产品和服务。

凭借AntChain Open Labs的技术支持,ZAN为企业和开发者提供了全面的技术产品和服务,其中包括智能合约审计(ZAN Smart Contract Review)、电子身份认证eKYC(ZAN Identity)、交易验证(ZAN Know Your Transaction)以及节点服务(ZAN Node Service)等。

通过ZAN的一站式解决方案,用户可以享受到全方位的Web3技术支持。



!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
联系我们
CONTACT US
!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析

!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析
“阅读原文” 获取更多联系方式 !

原文始发于微信公众号(ZAN Team):!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析

版权声明:admin 发表于 2023年12月8日 下午6:45。
转载请注明:!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...