这篇文章最初发布在以太坊博客.
这篇博文提供了我们对上周发现的存储损坏漏洞的调查结果的更新。总而言之,该漏洞的严重程度远低于我们最初的估计。我们发现的少量受影响合约要么只能被所有者利用,要么只能导致用户界面中断,而不会影响实际的合约逻辑。我们审查的所有可利用合约/dapp 都可以在无需升级合约本身的情况下修复。当然,为了安全起见,请仍然检查您的合约。
在发现 Solidity 编译器中的存储损坏漏洞并意识到它可能对已部署且无法更新的合约造成严重影响后,我们开始分析该漏洞的普遍程度以及如何解决可利用的合约。
我们重点关注在 etherscan 上发布了源代码的合约,因为重要或流行的智能合约通常会在那里发布其源代码以获得用户的信任,然后用户可以验证编译过程。此外,如果源代码不可用,攻击者也更难找到合适的利用方法。最后,私下使用的合约(因此不需要发布其源代码)通常会检查它们是否来自特定地址,因此攻击者无法写入其存储。
为了自动化检查 etherscan 上所有合约的过程,我们创建了一个修改版的 Solidity 编译器,它可以自动检测触发该漏洞的条件。这项技术已经将潜在易受攻击的合约数量减少到 167 个。然后,我们手动检查了这些合约是否存在可能导致它们易受攻击的存储损坏。
事实证明,只有十个合约存在漏洞,因此我们能够联系到大多数合约所有者/开发人员。这十个合约中有七个只能被所有者利用,因为它们允许所有者更改某些参数超出其允许的范围,或允许解锁先前锁定的合约。一个合约可被非特权用户利用,但其设计存在其他重大缺陷。另外两个被发现可被非特权用户利用的合约,要么在被利用时没有提供任何优势,要么只影响了用户界面。
为什么只有这么少的合约可被利用?
首先,让我们定义一下我们所说的“可利用”是什么意思。
如果存储损坏漏洞可以用来以在没有该漏洞的情况下不可能的方式修改存储中的变量,并且此修改对智能合约的行为和使用产生影响,则该漏洞可被利用。例如,我们在以下情况下不认为合约可被利用
- 相同账户可以通过常规方式在合约的相同状态下覆盖变量。
- 覆盖只能在构造时间发生(请注意,我们没有检查覆盖是否在那个时间发生)。
- 覆盖仅在合约逻辑本身已损坏的情况下触发(例如,每块递增一次的 32 位计数器溢出)。
- 可以覆盖智能合约中未使用的且看起来不重要的变量,但它们可能是公共接口的一部分。
为什么这个严重的漏洞在很少的情况下才能被利用?
这是以下因素的组合,这些因素共同作用并大大降低了可利用的可能性。
- 由于小型类型仅在极少数情况下提供优势,因此很少使用。
- 小型类型必须在存储中彼此相邻 - 它们之间的一个大型类型会阻止漏洞被触发。
- 状态变量通常一个接一个地分配,这会在第二次分配时消除损坏。
- “地址”和“布尔”的组合在剩余的案例中最常见,但在这种情况下,地址变量通常是“所有者”,它从msg.sender分配,因此不可利用。即使可以更改所有者,该标志通常仍然可以通过其他方式由所有者设置。
如何修复受影响的合约
绝大多数可利用合约只能被合约所有者、管理员或开发人员利用,特别是通过允许更改所有者的单个函数。该漏洞允许所有者进一步提升权限。为了防止所有者利用此漏洞,可以在所有者和受影响合约之间安装一个代理合约。此代理合约转发来自所有者的调用,但禁止调用可利用的函数。如果仍然需要调用可利用的函数,代理合约可以防止恶意数据被转发到合约。
如果您对您的合约有任何具体问题或疑虑,请在 Gitter 上联系我们。
法律友情重要提示
这篇文章中的陈述是针对 Solidity 编译器中的存储损坏漏洞提出的建议。如您所知,我们正在一个新兴且不断发展的技术领域工作。使这项工作令人兴奋的相同因素——创新、影响和对合约功能的日益增长的理解——也是使其具有风险的相同因素。如果您选择实施这篇文章中的建议并继续参与,您应该确保了解它如何影响您的特定合约,并且您应该了解其中存在的风险。通过选择实施这些建议,您独自承担后果的风险。