前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >以太坊智能合约安全漏洞(1):重入攻击

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

作者头像
Henry Zhang
发布2019-04-12 17:32:53
2K0
发布2019-04-12 17:32:53
举报
文章被收录于专栏:亨利笔记亨利笔记

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

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

本文首发于哈希1024社区:

https://hash1024.org/topics/27

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

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

程序间接递归

智能合约重入攻击

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

“Re-entrancy”的名称来自这样的现实:外部恶意合约回调了受攻击合约上的一个函数,并在受攻击合约上的任意位置“重新进入”代码执行。因为原合约的程序员可能没有预料到合约代码可以被“重入”,因此合约会出现不可预知的行为。 为了说明这一点,考虑以下简单易受攻击的合约,该合约充当以太坊金库,允许存款人每周仅提取最多1个以太币。 EtherStore.sol:

该合约有两个公共函数:depositFunds() 和 withdrawFunds() 。depositFunds() 函数只是累计发送者余额。withdrawFunds() 函数允许发送者指定要提取的以太币数量(wei为单位)。只有当要求提取的金额小于或等于1个以太币、并且在一周内没有发生提取时,它才会成功。真的是这样吗? 漏洞出现在第17行,我们向用户发送他们要求的以太数量。考虑一个恶意攻击者创建以下合约: Attack.sol:

我们来看看这个恶意合约如何利用 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] = 1 Ether。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() 函数仅发送 2300 Gas 给外部调用,这不足以使目的地址合约调用另一个合约(即重入原合约)。 第二种,是确保所有改变状态变量的逻辑,都发生在以太币被发送出合约(或任何外部调用)之前。在 EtherStore 示例中,EtherStore.sol 的第18和19行应放在第17行之前。最好将对未知地址的外部调用,作为本地函数或代码的最后一个操作。这在以太坊文档中称为检查-效果- 交互(checks-effects-interactions)模式。 第三种是引入互斥锁。也就是说,添加一个状态变量,在代码执行期间锁定合约,防止重入调用。 将所有这些技术(不是全部都需要,但是为了演示目的都使用了)应用于EtherStore.sol,得到以下无重入漏洞的合约:

真实例子:The DAO事件 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


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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 亨利笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
区块链
云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档