浅谈智能合约evilReflex漏洞
字数 1032 2025-08-22 12:23:12

智能合约evilReflex漏洞深度解析与防御指南

0x01 漏洞概述

漏洞名称:evilReflex漏洞(call注入攻击)

漏洞危害:攻击者可通过该漏洞将存在漏洞的合约中的任意数量代币转移到任意地址,或执行其他未经授权的操作

影响范围:多个基于ERC223标准的智能合约

0x02 预备知识

智能合约的外部调用方式

call基础用法

<address>.call(bytes)  // 基本调用方式
<address>.call(函数选择器, arg1, arg2, ...)  // 带参数的调用

函数选择器(Function Selector)

函数选择器是函数签名的Keccak哈希的前4字节。例如:

function baz(uint32 x, bool y) public pure returns (bool r) {
    r = x > 32 || y;
}

计算函数选择器:

sha3.keccak256(b'baz(uint32,bool)').hexdigest()[0:8]  // 结果为cdcd77c0

0x03 漏洞原理

基本攻击模式

contract evilreflex {
    function info(bytes data) {
        this.call(data);
    }
    
    function secret() public {
        require(this == msg.sender);  // 只能合约自身调用
        // 敏感操作...
    }
}

攻击方法:

this.call(bytes4(keccak256("secret()")));  // 绕过权限检查

bytes注入攻击

function approveAndCallcode(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
    if(!_spender.call(_extraData)) {
        revert();
    }
    return true;
}

攻击者可利用:

  1. 调用address.transfer()转移代币
  2. 调用approval()实现任意代币授权

方法选择器注入

function logAndCall(address _to, uint _value, bytes data, string _fallback) {
    ...
    assert(_to.call(bytes4(keccak256(_fallback)), msg.sender, _value, _data));
    ...
}

EVM的call函数特性:

  • 自动忽略多余参数
  • 允许参数类型不严格匹配

示例:

pragma solidity ^0.4.0;

contract A {
    uint256 public aa = 0;
    
    function test(uint256 a) public {
        aa = a;
    }
    
    function callFunc() public {
        this.call(bytes4(keccak256("test(uint256)")), 10, 11, 12);
    }
}

尽管test()只需要1个参数,但传入3个参数仍能成功调用。

0x04 真实案例分析:ATN代币增发漏洞

漏洞代码

function transferFrom(address _from, address _to, uint256 _amount, bytes _data, string _custom_fallback) 
    public returns (bool success) {
    ...
    if (isContract(_to)) {
        ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
        receiver.call.value(0)(bytes4(keccak256(_custom_fallback)), _from, _amount, _data);
    }
    ...
}

function setOwner(address owner_) public auth {
    owner = owner_;
    emit LogSetOwner(owner);
}

modifier auth {
    require(isAuthorized(msg.sender, msg.sig));
    _;
}

function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
    if (src == address(this)) {
        return true;
    } else if (src == owner) {
        return true;
    } else if (authority == DSAuthority(0)) {
        return false;
    } else {
        return authority.canCall(src, this, sig);
    }
}

攻击步骤

  1. 调用transferFrom()函数,设置:

    • _custom_fallback = "setOwner(address)"
    • _from = 攻击者地址
    • _to = 合约自身地址
  2. 执行流程:

    • receiver.call()msg.sender设为合约地址
    • 绕过isAuthorized()检查(因为src == address(this)
    • 成功调用setOwner()修改合约所有者

0x05 漏洞防御方案

最佳实践

  1. 避免使用call函数:尽可能使用更安全的调用方式如transfer()send()

  2. 严格控制参数:如果必须使用call,确保传入的参数不可被外部完全控制

  3. 使用固定函数选择器:避免动态生成函数选择器

  4. 严格权限检查:不要仅依赖msg.sender == address(this)作为权限验证

安全代码示例

// 安全版本的approveAndCallcode
function safeApproveAndCallcode(address _spender, uint256 _value, bytes _extraData) returns (bool) {
    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
    
    // 限制可调用的函数
    bytes4 allowedFunc = bytes4(keccak256("receiveApproval(address,uint256,bytes)"));
    require(_extraData.length >= 4 && bytes4(_extraData) == allowedFunc);
    
    (bool success, ) = _spender.call(_extraData);
    require(success);
    
    return true;
}

0x06 相关漏洞扩展

evilReflex漏洞属于call函数滥用的一类问题,其他相关漏洞包括:

  1. 重入攻击(Reentrancy):通过fallback函数递归调用
  2. DoS攻击:通过call失败导致整个交易回滚
  3. Gas限制攻击:通过复杂call消耗所有gas

开发者应全面了解call函数的风险,在必要时使用更安全的模式如"检查-生效-交互"(Checks-Effects-Interactions)模式。

智能合约evilReflex漏洞深度解析与防御指南 0x01 漏洞概述 漏洞名称 :evilReflex漏洞(call注入攻击) 漏洞危害 :攻击者可通过该漏洞将存在漏洞的合约中的任意数量代币转移到任意地址,或执行其他未经授权的操作 影响范围 :多个基于ERC223标准的智能合约 0x02 预备知识 智能合约的外部调用方式 call基础用法 函数选择器(Function Selector) 函数选择器是函数签名的Keccak哈希的前4字节。例如: 计算函数选择器: 0x03 漏洞原理 基本攻击模式 攻击方法: bytes注入攻击 攻击者可利用: 调用 address.transfer() 转移代币 调用 approval() 实现任意代币授权 方法选择器注入 EVM的call函数特性: 自动忽略多余参数 允许参数类型不严格匹配 示例: 尽管 test() 只需要1个参数,但传入3个参数仍能成功调用。 0x04 真实案例分析:ATN代币增发漏洞 漏洞代码 攻击步骤 调用 transferFrom() 函数,设置: _custom_fallback = "setOwner(address)" _from = 攻击者地址 _to = 合约自身地址 执行流程: receiver.call() 将 msg.sender 设为合约地址 绕过 isAuthorized() 检查(因为 src == address(this) ) 成功调用 setOwner() 修改合约所有者 0x05 漏洞防御方案 最佳实践 避免使用call函数 :尽可能使用更安全的调用方式如 transfer() 或 send() 严格控制参数 :如果必须使用call,确保传入的参数不可被外部完全控制 使用固定函数选择器 :避免动态生成函数选择器 严格权限检查 :不要仅依赖 msg.sender == address(this) 作为权限验证 安全代码示例 0x06 相关漏洞扩展 evilReflex漏洞属于call函数滥用的一类问题,其他相关漏洞包括: 重入攻击(Reentrancy) :通过fallback函数递归调用 DoS攻击 :通过call失败导致整个交易回滚 Gas限制攻击 :通过复杂call消耗所有gas 开发者应全面了解call函数的风险,在必要时使用更安全的模式如"检查-生效-交互"(Checks-Effects-Interactions)模式。