BLOCKCHAIN_Reentrancy重入攻击
重入攻击
重入攻击与solidity语言本身的设计缺陷有关,同时也与合约的设计有关......
简单示例
可以先来看一个简单的重入攻击的合约
pragma solidity ^0.7.0; |
Bank合约中:
mapping (address => uint) private balances;是创建一个由地址到整数的映射,相当于一个字典,记录账户的余额信息
withdraw()函数判断需要取出的数值是否小于等于账本中记录的数值,若是则取出
deposit()函数则为调用该函数的用户存入传入的数值
getBalance()返回整个账本的余额,方便查看余额
Attack合约中:
Bank public bank;接受合约地址,实例化一个Bank合约,以便调用Bank中函数
attack()执行攻击步骤
fallback()关键函数
放到remix上编译,然后部署
先部署银行合约,部署的时候带上一些以太币,这里放10个,标识银行里总共有10个以太
点击getBalance查看以银行余额,10eth,部署成功
接着再部署Attack合约,合约里面需要带上1个以太,因为attack()里面需要存入1个以太到银行,构造函数中需要一个待攻击合约的地址,带上上面部署的银行合约地址
点击Attack,调用合约的Attack()进行攻击,再观察银行合约,余额已经为0
重入攻击成功
具体解析
在解析之前需要了解以太坊虚拟机EVM中的代码调用关系 ,在一个合约中调用另一个合约的函数时,解释器会去加载另一个合约的代码,类似于将另一个合约的代码整合到自己的合约中,当成自己的函数调用.
合约转账过程中,会调用一个带有数额value的无名函数,在这个函数中有会调用fallback()函数
,也就是转账过程分为两部分,转账函数(send()、call()、transfer())首确定收款合约的收款操作,然后再执行fallback()函数
在攻击合约中点击attack后,先调用bank.deposit{value:1 ether}();往银行中存入1个ether,接着调用bank.withdraw(1 ether);,从银行中取出1个ether.
在调用bank.withdraw()时,在银行合约中会有分为两个步骤,一个是收款,一个是调用fallback()函数,于是银行合约会在取款时调用攻击合约中的fallback()函数,继续withdraw()操作,由于上一层的withdraw()还没有完成,银行记录的账户余额并不会减少,还能符合下一层的withdraw(),于是就形成递归直到将银行中的钱全部取出.