最新消息:本站技术交流 QQ 群:28124927

重入攻击演练与防范

区块链/比特币 exchen 1675浏览 0评论

重入攻击演练与防范

重入是在合约 A 调用合约 B 时执行某个操作时,合约 B 会调用合约 A 的回退函数 fallback,然后合约 A 的回退函数 fallback 里又调用了合约 B 的某个操作,形成了一个循环。

重入会造成攻击形象,比如合约 A 调用合约 B 的取币函数,合约 B 调用 call 函数将币发送到合约 A,此时会调用合约 A 的回退函数 fallback,如果合约 A 在 fallback 函数里又调用了合约 B 的取币函数,此时合约 B 又会调用 call 将币发送到合约 A,如此循环,可以一直把合约 A 上的币取光。

重入攻击的示例代码: https://solidity-by-example.org/hacks/re-entrancy

具体代码

下面我们来看一下具体的代码,合约 A 名称是 EtherStore,定义了三个函数,第一个是 deposit 用于存币,第二个是 withdraw 用于取币,第三个 getBalance 是用于查询本合约上币的数量,代码如下:

接下来来看一下合约 B,它是攻击合约,名称为 Attack,构造函数需要填写合约 EtherStore 的地址。有三个函数,一个是 attack 函数,该函数会调用合约 EtherStore 的 deposit 函数存入一个 ETH,然后再向合约 EtherStore 取币。第二个函数是回退函数 fallback,该函数会先判断合约 A 的币大于或等于 1,就继续取币。

演练过程

将上面的合约 EtherStore 和合约 Attack 代码放到 remix 上,先布署合约 EtherStore,再布署合约 Attack,布署合约 Attack 时需要填写合约 EtherStore 的地址,如下图所示:

调用 deposit 先给 EtherStore 合约存入 3 个 ETH,如下图所示:

再给 Attack 合约存入 1 个 ETH,然后再点击 Attack,先存入 1 个 ETH 到 EtherStore 合约,然后再调用 withdraw 取出 ETH,此时合约 EtherStore 里的 withdraw 函数 msg.sender.call 将币发送到合约 Attack,进入 Attack 的 fallback 回退函数,再执行 withdraw 取 ETH,形成了重入,如下图:

引发重入会一直循环把合约 EtherStore 里的币都取完,此时我们看一下 Attack 合约上的币就是 4 个 ETH,点击控制台的 Debug 还可以进入调试模式。

调试模式如下图,在代码的行号处点击可以添加断点,还能显示汇编指令,单步跟踪。

防范

关于防范重入有以下三个点需要注意。

(1)在示例代码中,EtherStore 合约在取币 withdraw 函数是先调用 call 发送币,再将用户币的数量置空,如果改成先将用户币置空,再调用 call 发送币,那么漏洞无法利用。

(2) 给取币函数 withdraw 添加一个函数修改器用于锁定状态,当执行 withdraw 函数时会先进入 noReentrant 修改器中,定义一个全局变量叫 locked,默认是 false,require 判断 locked 变量必须是 false,不然不会执行下面的操作,再将 locked 置为 true 锁定状态,执行完 withdraw 的功能,再将 locked 置为 false 用于解锁,中途要是有重入的情况不会得到执行。

(3) call 属于底层函数,一般情况下不要调用底层函数,比如转账最好是使用 transfer,这样也不会有重入的情况。

转载请注明:exchen's blog » 重入攻击演练与防范

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址