以太坊智能合约安全漏洞(1):重入攻击

Andrian Manning  2018-08-28  以太坊/以太坊(Ethereum)栏目  

以太坊智能合约安全漏洞(1):重入攻击

  题图摄于鼓浪屿:厦门夜色

  英文原文已有若干个中文翻译版本,但译文和原文都有错漏或不足。本文系基于译者(Henry)的理解,重新翻译并做了修正,并且加入了个人的注解和插图,力求让读者更容易领会原文的含义。为不影响阅读体验,注解没有单独标出, 可当译文 “笔记”来阅读。感兴趣的读者可参考文末的原文地址。

  本文首发于哈希1024社区:

  https://hash1024.org/topics/27

  虽然仍然处于起步阶段,但Solidity已被广泛采用,成为事实上的智能合约标准,新的区块链项目不少都兼容了Solidity 语言, Solidity 已经用于编写了大量的以太坊智能合约。由于语言和EVM的细微差别,开发人员和用户都体会到了许多深刻的经验教训。本文目的是作为相对深入和最新的介绍性文章,详细阐述Solidity开发人员如何吸取前人踩坑的教训,避免重蹈覆辙。

  重入问题(Re-Entrancy)

  以太坊智能合约能够调用和利用其他外部合约的代码。合约通常也处理以太币,因此将以太币发送到各种外部用户地址。调用外部合约或将以太币发送到地址的操作要求合约提交外部调用。这些外部调用可以被攻击者劫持,从而迫使合约执行更多的代码(即通过fallback 回退函数),包括回调原合约本身。所以,合约代码执行过程中将可以“重入”该合约,有点像编程语言里面的间接递归函数调用。在臭名昭著的TheDAO事件中黑客使用了这种攻击,最终导致了以太坊的硬分叉。

以太坊智能合约安全漏洞(1):重入攻击

  程序间接递归

以太坊智能合约安全漏洞(1):重入攻击

  智能合约重入攻击

  漏洞细节

  当合约将以太币发送到未知地址时,可能会发生此攻击。攻击者可以在外部地址小心地构建合约,该地址包含回退函数中的恶意代码。因此,当合约把以太币发送到此地址时,将激活恶意代码。通常,恶意代码在易受攻击的合约上执行函数,是开发人员没有预期的操作。

  “Re-entrancy”的名称来自这样的现实:外部恶意合约回调了受攻击合约上的一个函数,并在受攻击合约上的任意位置“重新进入”代码执行。因为原合约的程序员可能没有预料到合约代码可以被“重入”,因此合约会出现不可预知的行为。

  为了说明这一点,考虑以下简单易受攻击的合约,该合约充当以太坊金库,允许存款人每周仅提取最多1个以太币。

  EtherStore.sol:

以太坊智能合约安全漏洞(1):重入攻击

  该合约有两个公共函数:depositFunds() 和withdrawFunds() 。depositFunds() 函数只是累计发送者余额。withdrawFunds() 函数允许发送者指定要提取的以太币数量(wei为单位)。只有当要求提取的金额小于或等于1个以太币、并且在一周内没有发生提取时,它才会成功。真的是这样吗?

  漏洞出现在第17行,我们向用户发送他们要求的以太数量。考虑一个恶意攻击者创建以下合约:

  Attack.sol:

以太坊智能合约安全漏洞(1):重入攻击

  我们来看看这个恶意合约如何利用EtherStore合约。攻击者将使用EtherStore的合约地址作为构造函数参数创建上述合约(假设在地址0x0 ... 123处)。这将初始化并将公共变量etherStore,使其指向希望攻击的合约地址。

  然后攻击者将调用pwnEtherStore() 函数,并使用一些以太币(大于或等于1),例如1个以太币。在这个例子中,我们假设许多其他用户已将以太币存入此合约,这样它的当前余额为10个以太币。然后会发生以下情况:

  1. Attack.sol -第15行- EtherStore合约的depositFunds() 函数将被调用,其中msg.value为1 Ether(以及大量的Gas)。发件人(msg.sender)将是我们的恶意合约(0x0 ... 123)。因此,balances[0x0..123] = 1Ether。

  2.Attack.sol -第17行-然后恶意合约将使用1 ether的参数调用EtherStore合约的withdrawFunds()函数。这将通过所有require语句(EtherStore合约的第[12] - [16]行),因为我们之前没有提取过。

  3. EtherStore.sol -第17行-然后合约将1以太币发回恶意合约。

  4. Attack.sol -第25行-发送给恶意合约的以太币将执行回退函数。

  5.Attack.sol -第26行- EtherStore合约的总余额为10个以太币,现在为9个以太币,因此if语句通过。

  6.Attack.sol -第27行–回退函数再次调用EtherStore 的 withdrawFunds() 函数并“重新进入” EtherStore合约。

  7. EtherStore.sol -第11行-在第二次调用withdrawFunds() 时,我们的余额仍为1以太,因为第18行尚未执行。因此,我们仍然有balances[0x0..123] = 1Ether。lastWithdrawTime变量也是如此。我们再次通过了所有要求。

  8. EtherStore.sol -第17行-我们提取另外1个以太币。

  9.步骤4-8将重复-直到EtherStore.balance<= 1,如Attack.sol中的第26行所示。

  10. Attack.sol -第26行-一旦EtherStore合约中剩下不多于1(或更少)的ether,则此if语句将失败。然后,这将允许执行EtherStore合约的第18和19行(对于withdrawFunds()函数的每次调用)。

  11.EtherStore.sol –第18和19行-将设置balances和lastWithdrawTime映射,执行将结束。

  最终的结果是,攻击者通过这笔交易,立即从EtherStore合约中提取了所有以太币(只留下不超过1个以太币)。(本文首发于哈希1024社区:https://hash1024.org )

  预防技术

  程序员写合约时需要留个心眼,提防合约重入的可能性,采用一些技术避免智能合约中潜在的重入漏洞。

  第一种技术是(在可能的情况下)将ether发送到外部合约时使用内置的transfer() 函数。transfer() 函数仅发送2300Gas 给外部调用,这不足以使目的地址合约调用另一个合约(即重入原合约)。

  第二种,是确保所有改变状态变量的逻辑,都发生在以太币被发送出合约(或任何外部调用)之前。在EtherStore示例中,EtherStore.sol的第18和19行应放在第17行之前。最好将对未知地址的外部调用,作为本地函数或代码的最后一个操作。这在以太坊文档中称为检查-效果-交互(checks-effects-interactions)模式。

  第三种是引入互斥锁。也就是说,添加一个状态变量,在代码执行期间锁定合约,防止重入调用。

  将所有这些技术(不是全部都需要,但是为了演示目的都使用了)应用于EtherStore.sol,得到以下无重入漏洞的合约:

以太坊智能合约安全漏洞(1):重入攻击

  真实例子:TheDAO事件

  The DAO(Decentralized Autonomous Organization,去中心化自治组织)是以太坊早期发展中发生的主要黑客事件之一。当时,合约持有超过1.5亿美元的以太币。重入在攻击中发挥了作用,最终导致了以太坊经典(ETC)的硬分叉。

  关于The DAO攻击的完整始末,请参看本公众号 2016 年的4篇原创文章(可点击):

  道or悼?三分钟看懂史上最逆天的区块链众筹项目The DAO

  DAO可盗,非常道-惊天魔盗团和以太坊激战史上最大众筹

  软硬兼施,完币归赵-史上最大众筹项目The DAO黯然落幕

  The DAO后传:以太坊新旧链的平行世界

  欢迎扫码加入哈希1024社区微信群,进行更多的区块链技术交流(群满可加管理员:aristark 注明:哈希入群)

  参考文献:

  1. 英文原文:

  https://blog.sigmaprime.io/solidity-security.html

  2. The DAO 分析:

  http://hackingdistributed.com/2016/06/18/analysis-of-the-dao-exploit/

  3. Checks-effects-interactions 模式:

  https://solidity.readthedocs.io/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern

  4. 智能合约最佳实践:

  https://consensys.github.io/smart-contract-best-practices/known_attacks/#dos-with-unexpected-revert

  欢迎继续在文后留言交流,亨利笔记主要包含关于区块链、云计算的技术文章,欢迎关注:

以太坊智能合约安全漏洞(1):重入攻击

版权信息
作者:Andrian Manning
来源:亨利笔记

关于我们

联系我们

作者进驻

手机版

Copyright © 2013 比特巴 www.btb8.com