【区块链安全 | 第三十四篇】合约审计之重入漏洞

文章目录

    • 概念
    • 漏洞代码
    • 代码审计
    • 攻击代码
    • 攻击过程总结
    • 示例
    • 修复建议
    • 审计思路

【区块链安全 | 第三十四篇】合约审计之重入漏洞_第1张图片

概念

以太坊的智能合约可以互相调用,也就是说,一个合约可以调用另一个合约的函数。除了外部账户,合约本身也可以持有以太币并进行转账。当合约接收到以太币时,通常会触发一个叫做 fallback 的函数来执行一些特定的操作。这就是所谓的“隐蔽的外部调用”。

重入漏洞的问题出现在合约的外部调用上,尤其是当目标是一个恶意的合约时。攻击者可能会利用这种外部调用,在被攻击的合约中执行一些恶意逻辑。举个例子,当合约调用恶意合约时,恶意合约可以通过某些方式重新进入被攻击的合约,重复执行一些操作,甚至发起非预期的交易,从而破坏合约的正常逻辑。

换句话说,重入漏洞就是攻击者利用合约间外部调用的机制,在合约中反复进入,造成不希望发生的行为,通常会导致合约的资金被盗取。

漏洞代码

以下为典型的存在重入漏洞的合约代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

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

    // 存款函数
    function deposit() public payable {
        // 增加余额
        balances[msg.sender] += msg.value;
    }

    // 提款函数
    function withdraw() public {
        // 提款前,余额需大于0
        uint bal = balances[msg.sender];
        require(bal > 0, "Insufficient balance");

        // 先发送以太
        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

		// 再清除余额
        balances[msg.sender] = 0;
    }

    // 查询合约余额
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

不难看出,该 EtherStore 合约是一个充提合约。

代码审计

我们重点关注 withdraw() 函数:

    // 提款函数
    function withdraw() public {
        // 提款前,余额需大于0
        uint bal = balances[msg.sender];
        require(bal > 0, "Insufficient balance");

        // 先发送以太
        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether

你可能感兴趣的:(区块链安全,区块链,安全)