zoukankan      html  css  js  c++  java
  • 智能合约常见攻击方式

    准备

    普通攻击

    fallback回退函数

    合约可以有一个未命名函数,该函数不能有参数,也不能有返回值。fallback函数在以下情况会被调用:

    • 一个调用中,没有其他函数与给定的函数标识符匹配(或没有提供调用数据)。由于Solidity中,Solidity提供了编译期检查,所以我们不能直接通过Solidity调用一个不存在的函数。但我们可以使用Solidity的提供的底层函数address.call来模拟这一行为,进行函数调用。
    • 合约收到以太币,没有任何数据(为接受以太币,fallback函数必须标记为payablefunction() payable public{},否则合约无法接收,如果通过转账函数transfer发送到没有定义payable的合约,会抛出错误,导致后面的代码无法执行!但如果有合约通过自毁selfdestruct(address)的方式发送,即使没有定义为payable都得收下!)。通过MetaMask的Send向合约地址转以太币触发(ethernaut环境下可通过集成函数contract.sendTransaction({value: 1})转以太币;通过address.call.gas(1000000).value(msg.value)();在合约中转以太币)。

    构造函数

    构造函数无法显式调用,但如果构造函数和contract名字不一致,就能被直接调用,solidity 0.4.22引入了关键词constructor来指定构造函数。

    随机数预测

    • 使用区块变量block.coinbase/difficulty/gaslimit/number/timestamp,或基于过往块的哈希值block.blockhash(block.number – 1),由于区块变量在同一区块是共用的,通过攻击合约调用目标合约,即可实现预测
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    contract CoinFlip {
      function flip(bool _guess) public returns (bool);
    }
    contract Attack {
      address _addr;
      constructor(address param) { _addr = param;}
      function guess() public{
        CoinFlip cf = CoinFlip(_addr); //new instance with address
        uint256 blockValue = uint256(block.blockhash(block.number-1)); //generate the same random value
        bool side = uint256(uint256(blockValue) / FACTOR) == 1;
        return cf.flip(side); //invoke method with instance
      }
    }
    
    • 使用未来区块的区块哈希(使用上一次调用区块高度计算哈希值),由于EVM 能存储的区块哈希为最近的256条,超过的话置为 0。因此,如果第二次调用时,与第一次下注时的区块高度差超过了 256,那么此时的产生的区块哈希为0,此时伪随机数就变成可猜测的了
    • 使用私有的种子变量,将变量标记为私有只会阻止其他合约访问它,但是可以以链下的方式去获取链上的存储信息。如使用客户端的web3 API方法web3.eth.getStorageAt(addr, argument_index,callback),可以检索合约的存储
    1
    2
    3
    web3Provider = new Web3.providers.HttpProvider('https://ropsten.infura.io/'); //默认的web3必须带上callback回调函数
    web3 = new Web3(web3Provider);
    web3.eth.getStorageAt("0xad836dc9bc4fa0af947a27128edfa14459d77f19", 1, function(x, y) {大专栏  智能合约常见攻击方式pan class="nx">alert(web3.toAscii(y))}); //以alert的方式将该地址的第2个值转化ascii显示
    

    解决该问题可选的方案有RANDAOOraclize等,以去中心化的方式或是与外界互联网交互的方式得到安全的随机数。

    区分tx.originmsg.sender

    msg.sender是函数的直接调用方,在用户手动调用该函数时是发起交易的账户地址,但也可以是调用该函数的一个智能合约的地址。而tx.origin则必然是这个交易的原始发起方,无论中间有多少次合约内/跨合约函数调用,而且一定是账户地址而不是合约地址。所以如果存在用户通过合约A调用合约B,那么对应合约B而言,msg.sender 是合约 A 地址,但tx.origin 是用户的账户地址。

    如果目标合约使用tx.origin作为校验的依据,攻击者以钓鱼等方式,欺骗目标合约拥有者向攻击协议发送以太币,调用fallback函数,然后在fallback函数调用目标合约,由于tx.origin会是交易原始发起方,也就是目标合约拥有者,满足校验条件,从而实现攻击。

    解决方案:通过require(tx.origin == msg.sender)限制外部合约对内部合约的调用。

    整数溢出

    uint默认为256位无符整型,可表示范围[0, 2**256-1]。如果对0减1,则由256位的0,变成256位1,整数下溢,变成一个最大的整数。同理,如果对256位1加1,则变成256位0,变成最小的整数。同理:uint8,只能存储在范围[0,255]的数字。

    解决方案:

    • 在每一次数学运算时进行判断,如a=a+b;,就可以写成if(a+b>a) a=a+b;
    • 使用 OpenZeppelin 团队开发的SafeMath库,如果整数溢出漏洞发生时,函数将进行回退操作,如加法操作写为:a=a.add(b);

    call/delegatecall调用

    使用call函数来进行合约交互,对目标合约发送数据。delegatecall跟call主要的不同:通过delegatecall调用,仅使用目标地址的代码,其他信息则使用当前合约(如:msg.sender等)。delegatecall是危险函数,他可以在被调用合约完全操作原始合约的状态,谨慎使用!

    通过call/delegatecall调用函数,传入的第一个参数是四个字节时,会把这四个字节当作函数的id来寻找被调用函数,而一个函数id的生成规则是其函数签名的sha3的前4个bytes。因此通过:web3.sha3("pwn()").slice(0,10)=0xdd365b8b,加上0x,总共取前10个字符。

    重入攻击(Re-entrancy)

    被攻击函数withdraw()在发送以太币msg.sender.call.value(_amount)() 之后才更新余额balances[msg.sender] -= _amount; ,因此通过攻击合约调用withdraw时,攻击合约在fallback函数中接收以太币时再次调用withdraw,则可以在更新余额之前无限递归调用withdraw

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    contract Reentrance {
      function withdraw(uint _amount) public {
        if(balances[msg.sender] >= _amount) {
          if(msg.sender.call.value(_amount)()) {
            _amount;
          }
          balances[msg.sender] -= _amount; // update balance after send
        }
      }
    }
    contract Attack {
      function() public payable {
        uint weHave = c.balanceOf(this);
        if (weHave > c.balance) {
          if (c.balance != 0) c.withdraw(c.balance);
          return;
        }
        c.withdraw(weHave);
      }
    }
    
  • 相关阅读:
    The Python Standard Library
    Python 中的round函数
    Python文件类型
    Python中import的用法
    Python Symbols 各种符号
    python 一行写多个语句
    免费SSL证书(https网站)申请,便宜SSL https证书申请
    元宇宙游戏Axie龙头axs分析
    OLE DB provider "SQLNCLI10" for linked server "x.x.x.x" returned message "No transaction is active.".
    The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "xxx.xxx.xxx.xxx" was unable to begin a distributed transaction.
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12268129.html
Copyright © 2011-2022 走看看