重入漏洞相信大家都有所耳闻,那么什么是重入漏洞呢?

众所周知,以太坊的转账不仅可以在钱包地址之间进行,合约与钱包地址之间、合约与合约之间也可以,而合约在接收到转账的时候会触发 fallback 函数执行相应的逻辑,这是一种隐藏的外部调用。攻击者就会利用这一点,在合约的fallback 函数中写入恶意逻辑重新进入到被攻击的合约内部,让被攻击的合约执行非预期的外部调用,从而到达获取不正当利益的目的。

漏洞示例

下面我们来看一个比较典型的有重入漏洞的代码:

智能合约安全——重入漏洞

漏洞分析

上面的代码就是个普通的充提币的合约,那么怎么对其发起重入攻击呢?我们来看这个合约的 withdraw 函数,这个函数中的转账操作有一个外部调用“msg.sender.call{value: bal}”,所以我们就可以认为这个合约是可能有重入漏洞的,让我们来进一步分析看看:

在 withdraw 函数中是先执行外部调用进行转账后才将账户余额清零的,那么就可以在转账外部调用的时候构造一个恶意的逻辑合约在合约执行 balance[msg.sender]=0之前一直循环调用 withdraw 函数一直提币从而将合约账户清空。

攻击合约

下面我们看看攻击者编写的攻击合约中的攻击手法是否与我们的漏洞分析相同:

智能合约安全——重入漏洞

我们看到 EtherStore 合约是一个充提合约,我们可以在其中充值和提现。

攻击者先调用合约中的攻击函数先向EtherStore中充值1ether,在EtherStore中他的账户余额就为1ether,那么他就可以提现这些余额。紧接着,withdraw 函数发起提现,当EtherStore向攻击合约转账完成时,攻击合约就会调用fallback函数,再次请求提现余额,如此循环就能将EtherStore中的余额提现到不足1ether,才结束这个循环。

攻击函数调用流程图:

智能合约安全——重入漏洞

修复建议

看了上面的攻击手法相信大家对重入漏洞都会有一个自己的认知了,但是我们的应该怎么避免重入漏洞防御重入攻击呢?以下是我给大家的建议:

1. 写代码时需要遵循先判断,后写入变量在进行外部调用的编码规范(Checks-Effects-Interactions)。

2. 加入防重入锁。

下面是一个防重入锁的代码示例:

智能合约安全——重入漏洞

3. 记住所有涉及到外部合约调用的代码位置都是不安全的。

那么智能合约中的重入攻击就讲解完了,如果想了解更多的区块链知识,或是有什么疑问,可以到区块链交流社区CHAINPIP来,一起学习和交流。

社区地址:https://www.chainpip.com/