一、漏洞概述

1000 Guess是一款基于以太坊的随机数竞猜游戏。 1000 Guess中的simplelottery智能合约实现的‘_addguess’函数存在安全漏洞,该漏洞源于程序使用公共可读取的变量生成随机值。攻击者可利用该漏洞一直获取奖励。

下面为CVE编号的详细内容。

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12454

1000 Guess作为以太坊的精彩读博游戏被爆出存在存储随机数预测漏洞。此合约通过生成随机数来预测获得大奖的钱包地址。在生成随机数的过程中,该合约通过sha256计算合约中的变量与当前数据块的信息。然而根据区块链的概念,链上的信息均是公开的,所有用户均可以获得。于是攻击者可以对此进行获取并进行投机取巧的操作。在本文中我将对合约进行详细介绍,并对此合约漏洞进行复现操作。

二、合约分析

合约代码如下地址:https://etherscan.io/address/0x386771ba5705da638d889381471ec1025a824f53#code

/**
 * Source Code first verified at https://etherscan.io on Saturday, November 25, 2017
 (UTC) */

pragma solidity ^0.4.11;
contract simplelottery {
    enum State { Started, Locked }
    State public state = State.Started;
    struct Guess{
      address addr;
      //uint    guess;
    }
    uint arraysize=1000;
    uint constant maxguess=1000000;
    uint bettingprice = 1 ether;
    Guess[1000] guesses;
    uint    numguesses = 0;
    bytes32 curhash = '';
    uint _gameindex = 1;
    uint _starttime = 0;
    modifier inState(State _state) {
      require(state == _state);
      _;
    }
    address developer = 0x0;
    address _winner   = 0x0;
    event SentPrizeToWinner(address winner, uint money, uint gameindex, uint lotterynumber, uint starttime, uint finishtime);
    event SentDeveloperFee(uint amount, uint balance);

    function simplelottery() 
    {
      if(developer==address(0)){
        developer = msg.sender;
        state = State.Started;
        _starttime = block.timestamp;
      }
    }

    function setBettingCondition(uint _contenders, uint _bettingprice)
    {
      if(msg.sender != developer)
        return;
      arraysize  = _contenders;
      if(arraysize>1000)
        arraysize = 1000;
      bettingprice = _bettingprice;
    }

    function findWinner(uint value)
    {
      uint i = value % numguesses;
      _winner = guesses[i].addr;
    }

      function getMaxContenders() constant returns(uint){
      return arraysize;
    }

    function getBettingPrice() constant returns(uint){
      return bettingprice;
    }

    function getDeveloperAddress() constant returns(address)
    {
      return developer;
    }

    function getDeveloperFee() constant returns(uint)
    {
      uint developerfee = this.balance/100;
      return developerfee;
    }

    function getBalance() constant returns(uint)
    {
       return this.balance;
    }

    function getLotteryMoney() constant returns(uint)
    {
      uint developerfee = getDeveloperFee();
      uint prize = (this.balance - developerfee);
      return prize;
    }

    function getBettingStatus()
      constant
      returns (uint, uint, uint, uint, uint, uint, uint)
    {
      return ((uint)(state), _gameindex, _starttime, numguesses, getLotteryMoney(), this.balance, bettingprice);
    }



    function finish()
    {
      if(msg.sender != developer)
        return;
      _finish();
    }

    function _finish() private
    {
      state = State.Locked;
      uint block_timestamp = block.timestamp;
      uint lotterynumber = (uint(curhash)+block_timestamp)%(maxguess+1);
      findWinner(lotterynumber);
      uint prize = getLotteryMoney();
      uint numwinners = 1;
      uint remain = this.balance - (prize*numwinners);

      _winner.transfer(prize);
      SentPrizeToWinner(_winner, prize, _gameindex, lotterynumber, _starttime, block_timestamp);

      // give delveoper the money left behind
      developer.transfer(remain); 
      SentDeveloperFee(remain, this.balance);
      numguesses = 0;
      _gameindex++;
      state = State.Started;
      _starttime = block.timestamp;
    }

    function () payable
    {
        _addguess();
点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖