概念
以太坊的智能合约可以互相调用,也就是说,一个合约可以调用另一个合约的函数。除了外部账户,合约自己也可以持有以太币并举行转账。当合约接收到以太币时,通常会触发一个叫做 fallback 的函数来执行一些特定的操作。这就是所谓的“隐蔽的外部调用”。
重入漏洞的问题出现在合约的外部调用上,尤其是当目标是一个恶意的合约时。攻击者可能会使用这种外部调用,在被攻击的合约中执行一些恶意逻辑。举个例子,当合约调用恶意合约时,恶意合约可以通过某些方式重新进入被攻击的合约,重复执行一些操作,乃至发起非预期的生意业务,从而粉碎合约的正常逻辑。
换句话说,重入漏洞就是攻击者使用合约间外部调用的机制,在合约中反复进入,造成不盼望发生的举动,通常会导致合约的资金被盗取。
漏洞代码
以下为典型的存在重入漏洞的合约代码:
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.3;
- contract EtherStore {
- mapping(address => uint) public balances;
- // 存款函数
- function deposit() public payable {
- // 增加余额
- balances[msg.sender] += msg.value;
- }
- // 提款函数
- function withdraw() public {
- // 提款前,余额需大于0
- uint bal = balances[msg.sender];
- require(bal > 0, "Insufficient balance");
- // 先发送以太
- (bool sent, ) = msg.sender.call{value: bal}("");
- require(sent, "Failed to send Ether");
- // 再清除余额
- balances[msg.sender] = 0;
- }
- // 查询合约余额
- function getBalance() public view returns (uint) {
- return address(this).balance;
- }
- }
复制代码 不难看出,该 EtherStore 合约是一个充提合约。
代码审计
我们重点关注 withdraw() 函数:
- // 提款函数
- function withdraw() public {
- // 提款前,余额需大于0
- uint bal = balances[msg.sender];
- require(bal > 0, "Insufficient balance");
- // 先发送以太
- (bool sent, ) = msg.sender.call{value: bal}("");
- require(sent, "Failed to send Ether
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|