防止对以太坊的智能合约攻击——代码分析

约通常也处理以太,并且经常将以太发送到各种外部用户地址。这些操作要求合约提交外部调用。这些外部调用可能会被攻击者劫持,攻击者可以强制合约执行进一步的代码(通过一个回退函数),包括对自身的调用。

以太坊智能合约的特点之一是能够调用和利用来自其他外部合约的代码。

合约通常也处理以太,并且经常将以太发送到各种外部用户地址。这些操作要求合约提交外部调用。这些外部调用可能会被攻击者劫持,攻击者可以强制合约执行进一步的代码(通过一个回退函数),包括对自身的调用。

这类攻击被用于臭名昭著的DAO攻击。

了解漏洞

这种类型的攻击可能发生在合约将以太发送到未知地址时。攻击者可以在回退函数中小心地在包含恶意代码的外部地址上构造合约。

因此,当一个合约将以太发送到这个地址时,它将调用恶意代码。通常,恶意代码会在易受攻击的合约上执行一个函数,执行开发人员意想不到的操作。

术语“可重入性”源于此:外部恶意合约调用了易受攻击的合约上的一个函数,代码执行的路径“重新进入”了它。

为了澄清这一点,考虑一下易受攻击的EtherStore.sol,作为一个以太坊金库,存款人每周只能提取1个以太:

640 EtherStore.sol

这个合约有两个公共功能,depositFunds和withdrawFunds。

depositFunds函数只是增加发送方的余额。

withdrawFunds函数允许发送者指定要取款的wei。

只有当请求提取的金额小于1 个以太,且上周没有发生提取时,此函数才会成功。

漏洞在第17行,合约向用户发送了他们想要的以太的数量。

考虑一个在Attack.sol中创建合约的攻击者:

6401 Attack.sol

漏洞可能如何发生?

首先,攻击者会用EtherStore的合约地址作为唯一的构造函数参数来创建恶意合约(假设地址是0x0…123)。这将初始化并将公共变量etherStore指向要被攻击的合约。

然后攻击者会调用attackEtherStore函数,使用一些大于或等于1的以太——让我们暂时假设是1个以太。

在这个例子中,我们还假设许多其他用户已经将以太存入了这个合约,因此它的当前余额是10 以太。然后将发生以下情况:

  • Attack.sol的第 15 行:EtherStore的depositFunds函数将调用1 个以太(以及大量gas)的msg.value。发送方(msg.sender)将是恶意合约(0x0…123)。因此,balances[0x0..123] = 1 ether。
  • Attack.sol的第 17 行:恶意合约会调用参数为1个以太的EtherStore合约的withdrawFunds函数。这将通过所有的要求(EtherStore合约的第12-16行),因为之前没有进行过取款。
  • EtherStore.sol的第 17 行:将给恶意合约发送1个以太。
  • Attack.sol的第 25 行:支付给恶意合约的款项将执行回退功能。
  • Attack.sol的第 26 行:EtherStore合约的总余额是10个以太,现在是9个以太,所以这个if语句通过了。
  • Attack.sol的第 27 行:再次调用EtherStore的withdrawFunds函数,并“重新进入”EtherStore合约。
  • EtherStore.sol的第 11 行:在第二次调用withdrawFunds中,攻击合约的余额仍然是1个 以太,因为第18行还没有执行。因此,我们仍然有 balances[0x0..123] = 1 ether。lastWithdrawTime变量也是如此。我们再次通过了所有的要求。
  • EtherStore.sol的第 17 行:攻击合约再取1个以太。
  • 重复步骤4-8重复,直到不再出现EtherStore.balance > 1,如第 26 行中所指示的那样Attack.sol。
  • Attack.sol的第 26 行:一旦EtherStore合约中剩下1个(或更少)以太,这个if语句将失败。这将允许执行EtherStore合约的第18行和第19行(对于每个对withdraw函数的调用)。
  • EtherStore.sol,第 18 行和第 19 行:余额和lastwithdraw映射将被设置,执行将结束。

最终的结果是,攻击者在一个交易中从EtherStore合约中取出了除1个以太之外的所有以太。

预防的技术

有一些常见的技术可以帮助避免智能合约中潜在的重入漏洞。

第一个是(尽可能)在向外部合约发送以太时使用内置的转账函数。transfer函数通过外部调用只发送2300gas,这不足以让目的地址/合约调用另一个合约(即重新进入发送合约)。

第二种技术是确保所有更改状态变量的逻辑发生在以太被发送出合约(或任何外部调用)之前。在EtherStore的例子中,EtherStore.sol的第18和19行,应该放在第17行之前。

对于任何对未知地址执行外部调用的代码,最好将其作为本地化函数或代码执行段的最后一个操作。这就是所谓的检查-效果-交互模式。

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

在EtherStore中应用所有这些技术(没有必要使用这三种技术,但我们这样做是为了演示),给出了无重入的合约:

6402 EtherStore.sol

故事时间

DAO(去中心化自治组织)攻击是以太坊早期开发中发生的主要黑客攻击之一。

当时,该合约价值超过1.5亿美元。重入在攻击中扮演了主要角色,最终导致了硬分叉,从而创建了以太坊经典(ETC)。

更多关于以太坊分叉历史、DAO黑客时间线以及硬分叉中ETC的诞生的信息可以在(ethereum_standards)中找到。

Source:https://betterprogramming.pub/preventing-smart-contract-attacks-on-ethereum-a-code-analysis-bf95519b403a

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

Layer 2道友 - 欢迎对Layer 2感兴趣的区块链技术爱好者、研究分析人与Gavin(微信: chinadefi)联系,共同探讨Layer 2带来的落地机遇。敬请关注我们的微信公众号 “去中心化金融社区”

  • 发表于 2022-01-19 11:29
  • 阅读 ( 546 )
  • 学分 ( 1 )
  • 分类:智能合约

0 条评论

请先 登录 后评论
ChinaDeFi 去中心化金融社区
ChinaDeFi 去中心化金融社区

82 篇文章, 58 学分