{ 跳至内容 }

Solidity 存储数组漏洞

发布于 2019年6月25日 由 Solidity 和安全团队

安全警报

本文最初发表在以太坊博客.

这篇博文介绍了两个与存储数组相关的漏洞,它们之间没有关联。这两个漏洞在编译器中存在了很长时间,直到现在才被发现,尽管包含它们的合约很可能在测试中出现故障。

Daenam KimNguyen Pham 的帮助下,他们都来自 Curvegrid,发现了一个与带符号整数数组相关的存储无效数据的问题。

此漏洞自 Solidity 0.4.7 版本以来就存在,我们认为它是这两个漏洞中比较严重的一个。如果这些数组在特定情况下使用负整数,则会导致数据损坏,因此此漏洞应该很容易检测到。

通过以太坊漏洞赏金计划,我们收到了一份关于新的实验性 ABI 编码器(称为 ABIEncoderV2)中缺陷的报告。新的 ABI 编码器仍被标记为实验性,但我们仍然认为这值得发布公告,因为它已经在主网上使用了。感谢 Ming Chuan Lin(来自 https://www.secondstate.io)发现并修复了此漏洞!

0.5.10 版本 中包含了对这些漏洞的修复。目前,我们不打算发布针对 Solidity 0.4.x 系列的旧版本修复程序,但如果需求广泛,我们可能会发布。

这两个漏洞都应该在涉及相关代码路径的测试中很容易发现。

下面提供了关于这两个漏洞的详细信息。

带符号整数数组漏洞

谁应该关注

如果您部署了在存储中使用带符号整数数组的合约,并且要么直接分配

  • 一个至少包含一个负值的字面数组(x = [-1, -2, -3];)或者
  • 一个现有 不同 带符号整数类型的数组

给它,这将导致存储数组中的数据损坏。

仅分配单个数组元素(即使用 x[2] = -1;)的合约不受影响。

如何检查合约是否存在漏洞

如果您在存储中使用带符号整数数组,请尝试运行使用负值的测试。效果应该是存储的实际值是正数而不是负数。

如果您有一个满足这些条件的合约,并且想要验证该合约是否确实存在漏洞,您可以通过以下方式联系我们:security@ethereum.org.

技术细节

存储数组可以从不同类型的数组赋值。在此复制和赋值操作期间,会对每个元素执行类型转换。除了转换之外,特别是如果带符号整数类型短于 256 位,则必须将值的某些位清零,以准备将多个值存储在同一个存储槽中。

清零的位是从源类型而不是目标类型错误确定的。这会导致清零的位过多。特别是,符号位将被清零,这使得值变为正数。

ABIEncoderV2 数组漏洞

谁应该关注

如果您部署了使用实验性 ABI 编码器 V2 的合约,则这些合约可能会受到影响。这意味着只有在源代码中使用以下指令的合约才会受到影响

pragma experimental ABIEncoderV2;

此外,触发此漏洞还需要满足一些条件。有关更多信息,请参阅下面的技术细节。

如何检查合约是否存在漏洞

此漏洞仅在满足以下所有条件时才会出现

  • 涉及数组或结构体的存储数据直接发送到外部函数调用、abi.encode 或事件数据,而没有事先赋值给局部(内存)变量,并且
  • 此数据包含结构体数组或静态大小数组(即至少二维)。

此外,在以下情况下,您的代码不受影响

  • 如果您只返回此类数据,并且没有在 abi.encode、外部调用或事件数据中使用它。

可能的后果

当然,任何漏洞都可能产生千差万别的后果,具体取决于程序控制流,但我们预计这更有可能导致功能故障而不是可利用性。

在某些情况下,触发此漏洞会导致在方法调用时向其他合约发送损坏的参数。

技术细节

在编码过程中,实验性 ABI 编码器在元素占据多个存储槽的情况下无法正确前进到数组中的下一个元素。

仅当元素是结构体或静态大小数组时才会发生这种情况。动态大小数组或基本数据类型的数组不受影响。

您将看到的具体效果是数据在编码数组中“移位”:如果您有一个类型为 uint[2][] 的数组,并且它包含数据 [[1, 2], [3, 4], [5, 6]],则它将被编码为 [[1, 2], [2, 3], [3, 4]],因为编码器在元素之间仅前进一个槽而不是两个。

本文由 @axic@chriseth@holiman 共同撰写。

上一篇

下一篇

参与进来

GitHub

Twitter

Mastodon

Matrix

了解更多

博客文档使用案例贡献关于论坛

2024Solidity 团队

安全策略

行为准则