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行还没有执行。因此,我们仍然有
重复步骤4-8重复,直到不再出现 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中应用所有这些技术(没有必要使用这三种技术,但我们这样做是为了演示),给出了无重入的合约: 故事时间DAO(去中心化自治组织)攻击是以太坊早期开发中发生的主要黑客攻击之一。 当时,该合约价值超过1.5亿美元。重入在攻击中扮演了主要角色,最终导致了硬分叉,从而创建了以太坊经典(ETC)。 更多关于以太坊分叉历史、DAO黑客时间线以及硬分叉中ETC的诞生的信息可以在(ethereum_standards)中找到。
|