一个以太坊合约的漏洞分析-重入攻击

请找出下列合约漏洞,并说明如何盗取ContractB 中的数字资产,并修复合约。中说明:ContractB 的contract_a接口为ContractA 地址

复制代码
pragma solidity ^0.8.21;
interface ContractA {
    function get_price() external view returns (uint256);
}

interface ERC20 {

    function balanceOf(address) external view returns (uint256);

    function transfer(address, uint256) external returns (bool);
 }
 
 interface UniswapV2Pair {
     function transfer(address, uint) external returns (bool);
     function mint(address to) external returns (uint ) ;
     function burn(address to) external returns (uint , uint );
 }
 
 contract ContractB {
     ContractA contract_a;
     UniswapV2Pair _uniswapV2Pair;
     ERC20 token0;
     ERC20 token1;
     uint256 liquidity;
     address public _owner;
     mapping (address => uint256) private _balances;
     bool check=true;
     modifier noreentrancy(){
         require(check);
         check=false;
         _;
         check=true;
     }
     constructor(address owner){
         _owner=owner;
     }
    function setcontracta(address addr,address uniswapV2Pair,address _token0,address _token1) public  {
         require(msg.sender==_owner);
         contract_a = ContractA(addr);
         _uniswapV2Pair = UniswapV2Pair(uniswapV2Pair);
         token0=ERC20(_token0);
         token1=ERC20(_token1);
     }
 
     function depositFunds() public payable noreentrancy(){
         uint256 mintamount=msg.value*contract_a.get_price()/10e8;
         _balances[msg.sender]+=mintamount;
     }
 
     function withdrawFunds(uint256 burnamount) public payable noreentrancy(){
         _balances[msg.sender]-=burnamount;
         uint256 amount=burnamount*10e8/contract_a.get_price();
         msg.sender.call{value:amount}("");
     }
     function balanceof(address acount)public view returns (uint256){
         return _balances[acount];
     }
 
 }

 pragma solidity ^0.8.21;
 contract ContractA {
     uint256 private  _totalSupply;
     uint256 private _allstake;
     mapping (address => uint256) public _balances;
     bool check=true;

     modifier noreentrancy(){
         require(check);
         check=false;
         _;
         check=true;
     }
     constructor(){
     }
     function get_price() public view virtual returns (uint256) {
         if(_totalSupply==0||_allstake==0) return 10e8;
         return _totalSupply*10e8/_allstake;
     }

     function deposit() public payable noreentrancy(){
         uint256 mintamount=msg.value*get_price()/10e8;
         _allstake+=msg.value;
         _balances[msg.sender]+=mintamount;
         _totalSupply+=mintamount;
     }
 
     function withdraw(uint256 burnamount) public noreentrancy(){
         uint256 sendamount=burnamount*10e8/get_price();
         _allstake-=sendamount;
         payable(msg.sender).call{value:sendamount}("");
         _balances[msg.sender]-=burnamount;
         _totalSupply-=burnamount;
     }
 }

这段 Solidity 代码中存在一个关键的漏洞,允许攻击者在 ContractB 中盗取数字资产。攻击者可以利用 withdrawFunds 函数中的漏洞来实现攻击,让我们一步步分析和修复这个问题。

漏洞分析:

  1. withdrawFunds 函数中,合约从 _balances[msg.sender] 减去了 burnamount,然后使用 msg.sender.call{value:amount}(""); 从合约向调用者地址发送以太币。

  2. 这里的漏洞是没有重入保护机制的。攻击者可以通过多次调用 withdrawFunds 函数来进行重入攻击,每次都可以取回以太币并重复扣除 _balances[msg.sender]

攻击步骤:

  1. 攻击者在合约中调用 depositFunds 函数来存入以太币,生成相应数量的代币。

  2. 攻击者多次调用 withdrawFunds 函数,重入攻击使 _balances[msg.sender] 不断减少,而每次调用都会从合约中取回相同数量的以太币。

  3. 因此,攻击者可以重复取回代币,并获得更多的以太币。

修复漏洞:

为了修复这个漏洞,需要在 withdrawFunds 函数中添加重入保护。以下是修复后的代码:

solidity 复制代码
bool private checkReentrancy = true;

modifier noreentrancy() {
    require(checkReentrancy, "Reentrant call detected");
    checkReentrancy = false;
    _;
    checkReentrancy = true;
}

function withdrawFunds(uint256 burnamount) public payable noreentrancy() {
    require(_balances[msg.sender] >= burnamount, "Insufficient balance");
    
    _balances[msg.sender] -= burnamount;
    uint256 amount = burnamount * 10e8 / contract_a.get_price();
    
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
}

在修复的代码中,我们在 withdrawFunds 函数内添加了一个 checkReentrancy 布尔变量,用于保护函数免受重入攻击。在函数调用之前,我们要求 checkReentrancytrue,并在函数开始时将其设置为 false,以防止多次进入。

注意:

  • 重入攻击是一种常见的智能合约漏洞,请务必在编写合约时采取适当的重入保护措施。
  • 此处提供的修复只是一个示例,根据实际情况和需求可能需要进行进一步的安全性和逻辑审查。
  • 在部署和使用智能合约时,务必注意审查合约代码,测试合约的各种情况,以确保安全性和可靠性。
相关推荐
长安链开源社区3 小时前
学者观察 | 基于区块链的隐私计算技术——北京理工大学教授祝烈煌
运维·区块链
链上日记8 小时前
WEEX行业视角:从近期安全事件看,2026 年或成为行业安全分水岭
大数据·安全·区块链
m0_380167149 小时前
如何用 CoinGlass API 构建交易信号系统
人工智能·ai·区块链
m0_380167149 小时前
如何用订单簿数据判断真假突破(OrderBook 实战)
大数据·人工智能·区块链
2301_776045239 小时前
什么叫流动性:数字货币与美股市场解读
人工智能·区块链
长安链开源社区19 小时前
长安链2.3.8生产版本发布,安全、开放、灵活的企业级区块链底座
安全·区块链
程序员李程峰21 小时前
基础知识④链和代币之间的关系
web3·去中心化·区块链·智能合约·同态加密·共识算法·信任链
程序员李程峰1 天前
基础知识⑤ERC-20、BEP-20 和TRC-20 这三种流行的加密代币标准
web3·去中心化·区块链·智能合约·同态加密·共识算法·信任链
m0_380167141 天前
CoinGlass API 好用吗?(2026深度评估)
ai·区块链
长安链开源社区1 天前
动手开发 | 如何通过k8s部署长安链
云原生·容器·kubernetes·区块链