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的博客可以看到,目前已经存在数笔攻击交易,此处我们选取一笔交易进行详细分析。
1. 攻击中涉及的关键地址
攻击交易:https://etherscan.io/tx/0xecdd111a60debfadc6533de30fb7f55dc5ceed01dfadd30e4a7ebdb416d2f6b6
攻击者地址:
0xfde0d1575ed8e06fbf36256bcdfa1f359281455a
攻击合约:
0x6980a47bee930a4584b09ee79ebe46484fbdbdd0
TIME漏洞合约
0x4b0e9a7da8bab813efae92a6651019b8bd6c0a29
2. 攻击流程分析
2.1. 准备阶段
-
攻击者授权Uniswap V2 Router合约使用其TIME代币。
-
攻击者将5个ether存入WETH,获得5个WETH。
2.2. 攻击实施
-
攻击者调用Uniswap V2: Router合约的
swapExactTokensForTokensSupportingFeeOnTransferTokens
函数,意图使用5个WETH去换TIME。此次交换在Uniswap V2: TIME 40池子进行交换,攻击者换出了3455399346.269046个TIME。 -
攻击者调用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)
public
payable
returns (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);
}
-
观察交易得知,此时调用的是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");
}
-
调用到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
-
攻击者调用Uniswap V2: TIME 40池子的
sync
函数,将reserve量更新为实际池子中的代币数量。更新完以后,池子中TIME代币的reserve量变少,价格也变贵了。 -
攻击者再次在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等措施,规避该风险。
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技术支持。
原文始发于微信公众号(ZAN Team):!!紧急自查!!OpenZeppelin 任意地址欺骗攻击分析