本文深入探讨了 Anchor 框架中默认启用的 IDL 上传功能所带来的安全隐患。Anchor 默认添加了7个隐藏指令来支持 IDL 上传,但其中的 IdlCreateAccount
和 IdlCreateBuffer
存在潜在风险,可能导致 IDL 接管攻击和类型混淆漏洞利用,文章详细解释了这些攻击的原理和利用方式,并提供了相应的防护建议。
看看下面的代码。该程序有多少个指令?
lib.rs
Cargo.toml
显而易见的答案是 1 个指令:initialize
。
但如果你回答 1,你就错了。事实上,正确的答案是 8 个指令。
为什么?好吧,Anchor 向你的程序添加了 7 个指令,以启用 IDL 上传。许多开发者不知道的是,这个功能默认是开启的。
什么是 IDL 上传? IDL(接口描述语言)是一个 JSON 文件,描述了用户如何与 Solana 程序交互。它描述了程序实现的不同的指令,以及调用它们所需的参数和账户。
理论上,这个 JSON 文件可以存储在任何地方。但是我们拥有区块链是为了什么呢?将 IDL 存储在链上每个程序的预定地址上是有意义的。
因此,Anchor 实现了一个功能,可以将 IDL 文件上传到链上账户,该账户由程序地址和种子 anchor:idl
派生而来。这样,用户可以通过从程序地址计算此账户地址来轻松找到程序 IDL。
现在你已经了解了我们在说什么,让我们看看这个 IDL 上传功能是如何实现的。正如我之前提到的,有 7 个“隐藏”指令被添加到 Anchor 程序中:
除了这些指令之外,还有一种新的账户类型,IdlAccount
,它有三个字段,authority
,data_len
和 data
:
IdlAccount 结构。data 字段未在代码库中显式声明
虽然可以有很多这种类型的账户,但有一个主要的 IdlAccount
,它包含 IDL。它的地址从种子 anchor:idl
派生而来。除了主要的 IdlAccount
之外,还可以有任意的 IDL 缓冲区账户,它们是位于任意地址的 IdlAccount
。这个想法是将新的 IDL 分块上传到缓冲区账户,然后可以一步更新主 IdlAccount
。
在理解了这个过程之后,这 7 个指令确实有意义:IdlCreateAccount
用于初始化主 IdlAccount
,而 IdlResizeAccount
用于重新分配它。IdlCreateBuffer
用于创建非主 IdlAccount
,称为缓冲区,而 IdlCloseAccount
用于关闭缓冲区,甚至包括主 IdlAccount
。IdlWrite
将一个块写入缓冲区,而 IdlSetAuthority
更改 IdlAccount
的权限。最后,IdlSetBuffer
将缓冲区复制到主 IdlAccount
。
它是完全管理 IDL 上传系统的最小指令集,包括缓冲区和权限管理。而且大多数情况下,这很好。
但是,如果这里没有大问题,我就不会写这篇文章了:两个无需许可的功能,IdlCreateAccount
和 IdlCreateBuffer
。
是的,你没看错。IdlCreateAccount
,用于为程序创建主 IDL 账户并分配其 IDL 权限的函数,是无需许可的。任何人都可以调用它并将自己设置为该程序 IDL 的主人。需要注意的是,它只能被调用一次。这意味着在理想世界中,开发者会上传他们的合约并立即调用此指令来设置 IDL 账户。但现实是不同的。Solana 主网上有无数的程序启用了 Anchor 的 IDL 上传功能(这是一个默认设置),但从未调用过此指令。这对受影响的项目意味着什么?这意味着攻击者可以阻止这些项目上传 IDL,更糟糕的是,这意味着攻击者可以上传恶意 IDL,这可以用来欺骗任何尝试使用链上 IDL 的人。(在我们的下一篇博文中阅读更多关于恶意 IDL 攻击的信息)。我将这种类型的攻击称为 IDL 接管攻击。请注意,这不是一个新问题,事实上,Anchor 开发者已经知道这个问题多年了。
Github issue discussing IDL authority improvements from 2021
https://github.com/coral-xyz/anchor/issues/444#issuecomment-957056279
这种攻击的另一个版本只是攻击前端的工具,例如浏览器,以显示不正确的信息。例如,浏览器可能会使用链上 IDL 来确定某个指令签名被称为 “deposit”,因此它会将该指令显示为 “deposit” 指令。但是,恶意 IDL 可能会将显示的名称更改为 “accretion.xyz”,并在有人查看涉及此指令的交易时生成免费广告。我为我自己的 Ore 挖矿程序生成了这样一个虚假的 IDL。结果:应该显示为 “Mine” 的指令显示为 “Accretion.xyz”。
我的程序在官方 Solana 浏览器中将指令显示为 Accretion.xyz,即使它应该是 “Mine”
(此外,我已将上面 IDL 中的第一个账户重命名为 “The Boss” 而不是 “Authority”)。为了证明我的观点,我从一个与该程序完全无关且没有特殊权限的账户上传了这个 IDL。任何人都可以让浏览器为我的程序显示任何标签。
我认为稍微危险的第二个指令是 IdlCreateBuffer
。它本身是无害的。但它为利用 Solana 程序中的类型混淆(有时称为类型角色扮演)攻击提供了一个强大的原语。前提是你的程序有另一个容易受到类型混淆攻击的指令。 这意味着这个其他易受攻击的指令加载一个账户而不检查账户的鉴别器,只检查账户的所有者。只有当我们有另一个可以合法地作为另一个账户类型传递给程序的账户类型时,这种漏洞通常才是可利用的。这意味着它应该具有理想情况下相同的长度和任意可控制的数据。
IdlCreateBuffer
允许任何人创建由你的程序拥有的账户,这些账户具有几乎完全任意的内容,并且具有几乎任意的账户长度。记住 IDL 账户的布局,我们看到攻击者几乎完全控制了内容。他们可以将权限设置为任意字节,并且数据无论如何都是任意的。只有 data_len
字段必须是以下数据的实际长度,并且不能完全是任意的。
可以使一个 IdlAccount 包含几乎完全任意的数据,其任意长度仅受最小值的限制
现在想象一下,我们有一个易受攻击的函数,它期望给定的账户具有以下布局(有关鉴别器检查和安全最佳实践的更多信息,请参阅我们的 100 Solana tips)
我们将尝试角色扮演的假想账户类型
易受攻击的函数将加载此账户,检查账户所有者是否为程序,以及程序是否具有正确的大小,但不检查账户鉴别器。为了利用这一点,攻击者将需要在同一程序中具有相同长度的另一种账户类型,并且能够控制字段内容,这种情况很少见。但是,使用 IdlCreateBuffer
,攻击成为可能。(有关这些安全检查的计算成本,请参阅我们的 cost of security analysis。)
让我们看看我们将如何利用这一点:
创建缓冲区账户。 首先,攻击者将创建一个新的 84 字节的账户,使用 IdlCreateBuffer
初始化它,并将自己设置为此缓冲区的权限。因为账户大小为 84 字节,所以 data_len
将为 84 减去鉴别器长度和权限长度,因此 data_len = 44, or 0x0000002c
。此时的数据将只是零。该账户将如下所示:
新创建的 IdlAccount 缓冲区,正确解码为 IdlAccount
将同一账户重新解释为 DepositReceipt
,变为
与上面相同的新创建的 IdlAccount 缓冲区,解码为存款收据
接下来,我们继续创建一张价值 100 万美元 USDC 的虚假存款收据。为此,我们调用 IdlSetData
函数,并将数据设置为 USDC mint 和 100 万美元 USDC。账户内容变为:
IdlAccount,在我们更改数据以解码为 100 万美元 USDC 存款后
接下来,攻击者会挖掘一个以 0x2c000000
结尾的 vanity key(因为是小端序)。我们假设挖掘的 vanity key 是十六进制 0xBBBBBB...BBBB2c000000
。在我们的最后一步中,我们将通过更改 32 字节的缓冲区权限来创建一个虚假的时间戳(0x11223344
)并写入 vanity key 的前 28 个字节。我们最终得到以下账户,它既是一个 IdlAccount(除了鉴别器),也是我们 vanity 地址的 100 万美元 USDC 的有效存款收据。
在最后一步之后,我们将 IdlAccount 权限设置为时间戳和 vanity key 的组合,这将完成角色扮演
攻击者现在可以使用虚假的 DepositReceipt
来利用我们假设容易受到类型混淆攻击的函数。
所有这些都表明,IDL 缓冲区为攻击者提供了一个非常强大的工具,可以利用启用了 IDL 上传的 Anchor 合约中的任何类型混淆漏洞,因为攻击者可以将几乎任意的数据以任意长度写入此账户。
再次,我想强调的是,如果没有完全独立的类型混淆漏洞,此 IDL 函数并不危险。
为了防止此类攻击,唯一的好方法是完全禁用 anchor 链上 IDL 功能。但是,如果你确信你的合约中没有类型混淆漏洞,因为你的每个指令都正确地强制执行所有账户类型,那么确保你在其他人之前声明主 IDL 账户就足够了。
如果你受到恶意 IDL 攻击的影响,或者其他人声明了你的 IDL 账户,你唯一的补救方法是使用新的自定义指令执行程序升级,该指令将 IDL 权限转移到你的账户。
总的来说,我们认为这是 Anchor 的一个设计问题。我们已经联系了他们的团队,并建议默认情况下删除指令,理想情况下将 IdlCreateAccount
指令限制为程序权限或其他在编译期间硬编码的账户。Anchor 团队告诉我们,他们已经意识到了这个问题,并且修复程序将在 v0.31.0 版本中发布,该版本即将发布。
- 原文链接: accretion.xyz/blog/hidde...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!