本文分析了三个近期Web3安全事件:TheIdolsNFT由于相同地址对参数管理不当导致奖励重复领取漏洞,Alien Base Dex由于三明治攻击导致资金损失,以及ERC-6492通过预编译合约的任意调用漏洞导致5万美元损失。文章旨在通过案例分析,提高社区对Web3安全漏洞的认识和防范能力。
介绍 欢迎来到 The Notorious Bug Digest,这里简要收集了最近 Web3 的漏洞和安全事件分析!在不进行审计时,我们的一些安全研究员会花时间了解最新的安全动态,阅读漏洞报告并研究链上事件。我们认为这些信息对安全社区很有用,可以帮助其他研究人员磨练技能或进入安全领域。因此,我们决定对外分享这些内容。 TheIdolsNFT 事件,由相同的地址对参数管理不当引起
该 NFT 项目具有一个收益和申领机制,如图片 A 所示。rewardPerGod 跟踪自协议开始以来,每个 NFT 获得的 stETH 累计数量。claimedSnapshots 是一个映射,记录了每个用户为每个 NFT 申领了多少奖励。当用户申领他们的奖励时,_claimEthRewards 函数根据用户持有的 NFT 数量以及 rewardPerGod 和用户的 claimedSnapshots 之间的差额来计算奖励。计算完成后,它会更新用户的 claimedSnapshots 以匹配 rewardPerGod。
如图片 B 所示,在 token 转移操作期间,_beforeTokenTransfer Hook会自动为发送者和接收者申领奖励。如果发送者在转移后最终没有持有任何 NFT,他们的 claimedSnapshots 将重置为零。但是,当发送者和接收者是同一地址,并且他们只持有一个 NFT 时,接收者的 claimedSnapshots 会在奖励被申领之前重置为零,从而允许接收者申领更多的奖励。
图片 C 说明了攻击者如何通过在相同的发送者和接收者地址之间重复转移单个 NFT 来利用这个漏洞,从而积累不正当的利润。



Alien Base Dex 事件,源于 Sell-AddLiquidity-Buy 三明治攻击
如图片 A 所示,该合约具有一个 compound 函数,旨在从流动性池中收集费用并将其再投资。但是,此函数是无需许可的;tick 范围由调用者决定,并且没有滑点保护 (amountMin = 0)。图片 B 概述了攻击序列:攻击者首先出售闪电贷的 ALB token,这降低了 ALB/WETH 的价格。然后,他们触发 compound 函数,以这种人为的低价增加流动性。之后,攻击者用 WETH 回购 ALB,利用较低价格下增加的流动性,最终获得比开始时更多的 ALB。最后,攻击者在另一个池中将这些 ALB token 转换回 WETH,以确保他们的利润。


通过预编译合约利用 ERC-6492 任意调用
一个使用 ERC-6492 的项目被攻击,导致损失 5 万美元。虽然根本原因(任意调用)很简单,但利用方式很有趣,因为它使用预编译合约来绕过正常的接口调用。ERC-6492 通过允许对尚未部署的合约(称为反事实合约)进行签名验证来扩展 ERC-1271。该过程涉及在使用类似 concat(abi.encode((create2Factory, factoryCalldata, originalSignature)), magicBytes) 的格式,当合约不在链上时,使用附加的部署数据包装签名,其中 magicBytes (0x6492...6492) 表示这种特殊的验证。验证者首先检查这些 magic bytes,以查看在验证之前是否需要部署。如果合约已经部署,则使用标准的 ERC-1271 验证;否则,调用带有 factoryCalldata 的 create2Factory 来部署它。
ERC-6492 还为验证者提供了一个参考实现,该项目使用了简化的版本。根据代码,如果签名中有一个特定的后缀,并且签名者合约尚未部署 (code.length == 0),则验证者会调用 create2Factory。请注意,create2Factory 和 factoryCalldata 都来自外部用户,这引入了一个任意调用漏洞。但这实际上是可以利用的吗?

让我们假设 isCounterfactual 是 true 并且 _signer.code.length == 0,并且我们触发一个 USDC.transfer 调用以从验证者合约中耗尽 USDC token。然后,控制流进入 // Try ERC-1271 verification 分支并执行 try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate) returns (bytes4 magicValue)。由于实际上没有合约部署在 _signer 的地址上,isValidSignature 返回空数据,导致解码 revert,并且 hack 失败。第一个解决方法是使用带有Hook的 token。例如,ERC-1155 在转移时有一个接收者Hook onERC1155Received。如果 ERC-1155 token 留在验证者合约中,我们可以在 _signer 部署一个合约,该合约为 isValidSignature 调用返回数据。但是,如果合约只持有像 USDC 这样的 ERC-20 token 呢?在这里,我们无法在 USDC.transfer 期间操纵控制流,但我们需要 isValidSignature 成功。是否存在任何合约,其中 code.length == 0 但在使用 isValidSignature(bytes32, bytes) 调用时返回 >= 4 个字节?
code.length == 0 的合约是预编译!它们没有 EVM 代码,但有 节点代码。calldata。查看 EVM 中(Cancun)的 当前预编译,有几个潜在的候选者。例如,地址 0x4 的预编译接受字节数据并返回确切的输入;由于输入大于 4 个字节,因此解码成功!攻击者使用此方法来执行 hack。
如图片所示,他们使用 0x4 预编译作为 _signer 调用 isValidSigImpl,设置 create2Factory 和 factoryCalldata 为 USDC.transfer(attacker, balance)。这触发了转移,并允许 IERC1271Wallet(_signer).isValidSignature 调用在最后成功执行。
在此事件发生后不久,@pcaversaccio 提出了一个 issue,建议在官方 ERC-6492 参考实现中添加关于潜在漏洞的警告,包括结合任意调用使用预编译地址 (0x04)。
重要的是要强调,此内容的意图不是批评或指责受影响的项目,而是提供客观的概述,作为社区学习并更好地保护未来项目的教育材料。
- 原文链接: openzeppelin.com/news/th...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!