无需许可:Multichain漏洞利用解析

Multichain协议的用户遭到攻击,攻击者利用Multichain智能合约中的漏洞窃取了数百万美元的代币。文章深入分析了漏洞的技术细节,利用Tenderly调试器重放智能合约交易,揭示了攻击者如何通过构造恶意合约、绕过WETH合约的permit函数以及利用用户预先授权的无限额度来窃取资金。文章还总结了漏洞产生的多个因素,并提出了安全建议。

几天前,Multichain 的用户遭到多个攻击者团伙的攻击,他们都利用了 Multichain(以前的 AnySwap)智能合约中的同一个漏洞。 攻击者能够窃取价值数百万美元的代币。

加密协议公开宣布缺陷,用户遭到黑客无情攻击 \ 针对 Multichain 用户的黑客攻击正在恶化,因为一位网络安全研究人员称该事件为“最糟糕的方式... \ www.vice.com

在密切关注这次(非常有趣!)黑客攻击的细节的同时,我们希望在本文中重点关注此漏洞的技术方面。

工具:

为了分析这个漏洞,我们将主要使用 tenderly 的调试器,这是一个我最近才了解到的工具,似乎是以太坊研究人员工具箱的一个很好的补充。

Tenderly 调试器允许其用户重放智能合约交易的执行过程,包括所有参数、内部函数调用和当时的区块链状态,并逐步进入开源智能合约代码的每一行。

让我们直接开始分析一笔金额最高的漏洞利用交易,该交易导致 308 ETH(约 95 万美元)被盗。

攻击交易在 etherscan

让我们将此交易哈希提供给 Tenderly 调试器,看看它能告诉我们什么。

攻击交易在 Tenderly

调试器将我们引导到 Multichain 代码中的函数,攻击者可能滥用了该函数。 这是 anyswapRouterv4.sol 合约中的 anySwapOutUnderlyingWithPermit() 函数。

但是为了理解这种滥用,我们需要首先了解它的正常功能。

anySwapOutUnderlyingWithPermit() 讲解

根据 网站,Multichain(以前的 AnySwap,因此得名)路由器的任务是:

Multichain 路由器允许用户在任意两个链之间自由交换。 它可以降低费用,并使在链之间移动变得更容易。

为此,路由器使用其 “anyToken” 包装实际的 token。 例如,DAI token 被包装为 anyDAI,或者反之,DAI 是 anyDAI 的底层资产。 包装后的 token 用于 Multichain 内部会计,当用户将 DAI 从以太坊 “转移” 到 BSC 时,实际上 anyDAI 会添加到 Multichain anyDAI BSC 合约中,并在 anyDAI 以太坊合约上销毁(减少)。

前面提到的被利用的函数 anySwapOutUnderlyingWithPermit 使用 ERC20 permit() 函数 交换底层 token。 permit() 函数允许其用户提供已签名的交易,以批准合约花费其资金,而无需实际将其发送到区块链,这有助于最大限度地降低用户的 gas 成本。 签名交易用 (v,r,s) 术语表示。

易受攻击的函数(来源 github

在介绍之后,我们现在可以了解 anySwapOutUnderlyingWithPermit() 的功能:

  1. address _underlying = AnyswapV1ERC20(token).underlying(); 从其 anyToken 包装(“anyDAI”)中解包底层 token(“DAI”)
  2. IERC20(_underlying).permit(from, address(this), amount, deadline, v, r, s); 调用底层 token(“DAI”)的 ERC20 合约 permit() 以批准路由器(this)从用户(from)地址提取 amount 的能力,因为用户为此提供了签名交易,用 (v,r,s) 表示
  3. TransferHelper.safeTransferFrom(_underlying, from, token, amount); 如果我们到了这一行,则意味着上一行的签名已验证,现在我们可以使用它授予路由器的批准,以实际将金额从用户转移到包装的 token 帐户。

该函数的其余部分处理其包装版本的会计处理并在链之间发送。

值得注意的是,根据我们在 Dune Analytics 上创建的查询,此易受攻击的函数从未实际使用过,它的首次使用是 1 月 18 日的漏洞利用。 这意味着此函数实际上是一块死木,只会增加合约的攻击面。

anySwapOutUnderlyingWithPermit() 在 1 月 18 日攻击者使用它之前从未被使用过(来源:Dune Analytics

漏洞利用

现在让我们看看攻击者传递给易受攻击的函数的参数

攻击交易在 Tenderly 中的函数调用参数

from 是受害者的地址, token 是攻击者部署的合约, to 目标地址也是如此。

我们可以看到攻击者没有传递有效的签名,因为 v,r,s 都是零。

攻击者试图通过无效签名获得其合约的 308 ETH。 它是如何工作的?

  1. address _underlying = AnyswapV1ERC20(token).underlying(); 它旨在从其 anyToken 包装(“anyDAI”)中解包底层 token(“DAI”)。 但是, token 现在是攻击者控制的合约。 我们可以在调试器中看到,此合约现在将其 “底层资产” 返回为 WETH。 Multichain 在这里失败了,因为此函数应该检查 token 地址是否确实是 Multichain token

underlying() 输出是 WETH 合约地址 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2Tenderly

2. IERC20(_underlying).permit(from, address(this), amount, deadline, v, r, s); 最初 , 预期的结果是调用底层 token(“WETH”)的 ERC20 合约 permit() 以批准路由器(this)从用户(from)地址提取 amount 的能力,因为用户为此提供了用 (v,r,s) 表示的签名交易 但是,WETH 合约没有 permit() 函数! WETH 合约有一个 “fallback function”,当调用一个函数但未找到时会调用该函数。 WETH 的 fallback function 是 deposit(),在这种情况下不会执行任何实际操作,但允许其调用函数的执行继续,因为它不会失败。

WETH 合约没有 Permit(),回退到 deposit()(Tenderly

3. TransferHelper.safeTransferFrom(_underlying, from, token, amount); 最初,我们预计如果我们到了这一行,则意味着上面的签名已验证,现在我们可以使用它授予的批准来实际将金额从用户转移到路由器 但是,如上所述,签名未经过验证。 理论上,这不应该是一个问题,因为尽管攻击者的输入不应该通过签名验证,但它没有批准路由器访问代表受害者转移资金。 但是,Multichain 的 dapp 要求其所有用户提供实际上是无限的批准金额。 这种不安全的方法在 dapp 中非常常见,可以节省用户的 gas 费用。 我们过去曾警告说,如果存在恶意或易受攻击的 dapp,这种行为(我们将其命名为 baDAPProve)可能很危险,而现在这种潜在的威胁已经变为现实。通过滥用这种过度的批准,该函数将 WETH amount 从受害者帐户转移到攻击者控制的合约。

现在攻击者获得了受害者的资金,他们只需要确保该函数不会失败,并且此转移不会被回滚,这就是代码的其余部分所做的。

总结

正如事故和漏洞经常发生的那样,这里讨论的漏洞有多个促成因素,可能即使正确处理其中任何一个,都可以帮助 Multichain 的用户免受攻击:

  1. 易受攻击的函数实际上未使用,可以从一开始就删除。
  2. 易受攻击的函数没有验证 token 输入参数是否确实是有效的 Multichain token。
  3. 易受攻击的函数没有明确验证它是否确实成功调用了非强制性的 ERC20 permit() 函数。
  4. dapp 应用了 “BaDapprove” 行为,使其用户批准此易受攻击的合约无限访问其资金。

此外,Multichain 没有对此合约应用任何升级机制,因此 用户唯一的防线是单独撤销他们之前的批准。

Twitter 嵌入

Twitter 小部件 Iframe

我们希望通过分享这项研究和经验教训,我们都能享受更安全的 Web3 环境。

  • 原文链接: medium.com/zengo/without...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
TalBeerySec
TalBeerySec
江湖只有他的大名,没有他的介绍。