以太坊随机数平安周全剖析(二) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

以太坊随机数平安周全剖析(二)

申博_安全防护 申博 59次浏览 未收录 0个评论

一、媒介

本文继承前文的解说。在前文中我们引见了区块链中由公然变量做种子而引发的安全问题;有些合约运用区块哈希作为变量并将其放入合约函数中作为某种读博游戏的剖断照旧。

因为这些随机数并不是真正的“随机”,所以其安全隐患也是庞大的。本文我们继承引见四种随机数破绽范例。

二、基于区块哈希的随机数问题

block.blockhash(block.number-1)

很多合约运用blockhash作为临盆随机数的变量,并传入上一个区块编号。这类要领一样存在问题,进击者可以挪用雷同的要领来天生该随机数。

比方下面一个合约:

/**
 *Submitted for verification at Etherscan.io on 2016-04-23
*/

contract LuckyDoubler {
//##########################################################
//#### LuckyDoubler: A doubler with random payout order ####
//#### Deposit 1 ETHER to participate                   ####
//##########################################################
//COPYRIGHT 2016 KATATSUKI ALL RIGHTS RESERVED
//No part of this source code may be reproduced, distributed,
//modified or transmitted in any form or by any means without
//the prior written permission of the creator.
    event log(uint256);
    address private owner;

    //Stored variables
    uint private balance = 0;
    uint private fee = 5;
    uint private multiplier = 125;

    mapping (address => User) private users;
    Entry[] private entries;
    uint[] private unpaidEntries;

    //Set owner on contract creation
    function LuckyDoubler() {
        owner = msg.sender;
    }

    modifier onlyowner { if (msg.sender == owner) _ }

    struct User {
        address id;
        uint deposits;
        uint payoutsReceived;
    }

    struct Entry {
        address entryAddress;
        uint deposit;
        uint payout;
        bool paid;
    }

    //Fallback function
    function() {
        init();
    }

    function init() private{

        if (msg.value < 1 ether) {
             msg.sender.send(msg.value);
            return;
        }

        join();
    }

    function join() private {

        //Limit deposits to 1ETH
        uint dValue = 1 ether;

        if (msg.value > 1 ether) {

            msg.sender.send(msg.value - 1 ether);   
            dValue = 1 ether;
        }

        //Add new users to the users array
        if (users[msg.sender].id == address(0))
        {
            users[msg.sender].id = msg.sender;
            users[msg.sender].deposits = 0;
            users[msg.sender].payoutsReceived = 0;
        }

        //Add new entry to the entries array
        entries.push(Entry(msg.sender, dValue, (dValue * (multiplier) / 100), false));
        users[msg.sender].deposits++;
        unpaidEntries.push(entries.length -1);

        //Collect fees and update contract balance
        balance += (dValue * (100 - fee)) / 100;

        uint index = unpaidEntries.length > 1 ? rand(unpaidEntries.length) : 0;
        Entry theEntry = entries[unpaidEntries[index]];

        //Pay pending entries if the new balance allows for it
        if (balance > theEntry.payout) {

            uint payout = theEntry.payout;

            theEntry.entryAddress.send(payout);
            theEntry.paid = true;
            users[theEntry.entryAddress].payoutsReceived++;

            balance -= payout;

            if (index < unpaidEntries.length - 1)
                unpaidEntries[index] = unpaidEntries[unpaidEntries.length - 1];

            unpaidEntries.length--;

        }

        //Collect money from fees and possible leftovers from errors (actual balance untouched)
        uint fees = this.balance - balance;
        if (fees > 0)
        {
                owner.send(fees);
        }      

    }

    //Generate random number between 0 & max
    uint256 constant private FACTOR =  1157920892373161954235709850086879078532699846656405640394575840079131296399;
    function rand(uint max)  returns (uint256 result){
        uint256 factor = FACTOR * 100 / max;
        uint256 lastBlockNumber = block.number - 1;
        uint256 hashVal = uint256(block.blockhash(lastBlockNumber));
        log(hashVal);
        return uint256((uint256(hashVal) / factor)) % max;
    }


    //Contract management
    function changeOwner(address newOwner) onlyowner {
        owner = newOwner;
    }

    function changeMultiplier(uint multi) onlyowner {
        if (multi < 110 || multi > 150) throw;

        multiplier = multi;
    }

    function changeFee(uint newFee) onlyowner {
        if (fee > 5) 
            throw;
        fee = newFee;
    }


    //JSON functions
    function multiplierFactor() constant returns (uint factor, string info) {
        factor = multiplier;
        info = 'The current multiplier applied to all deposits. Min 110%, max 150%.'; 
    }

    function currentFee() constant returns (uint feePercentage, string info) {
        feePercentage = fee;
        info = 'The fee percentage applied to all deposits. It can change to speed payouts (max 5%).';
    }

    function totalEntries() constant returns (uint count, string info) {
        count = entries.length;
        info = 'The number of deposits.';
    }

    function userStats(address user) constant returns (uint deposits, uint payouts, string info)
    {
        if (users[user].id != address(0x0))
        {
            deposits = users[user].deposits;
            payouts = users[user].payoutsReceived;
            info = 'Users stats: total deposits, payouts received.';
        }
    }

    function entryDetails(uint index) constant returns (address user, uint payout, bool paid, string info)
    {
        if (index < entries.length) {
            user = entries[index].entryAddress;
            payout = entries[index].payout / 1 finney;
            paid = entries[index].paid;
            info = 'Entry info: user address, expected payout in Finneys, payout status.';
        }
    }

}

该合约重点部份为:

//Generate random number between 0 & max
uint256 constant private FACTOR =  1157920892373161954235709850086879078532699846656405640394575840079131296399;
function rand(uint max) constant private returns (uint256 result){
  uint256 factor = FACTOR * 100 / max;
  uint256 lastBlockNumber = block.number - 1;
  uint256 hashVal = uint256(block.blockhash(lastBlockNumber));
  return uint256((uint256(hashVal) / factor)) % max;
}

该函数为随机数天生函数,该函数设置了一个初始参数FACTOR用于对随机数举行开端处置惩罚。那末该随机数是怎样天生的呢?

这里合约中运用lastBlockNumber = block.number - 1以及uint256(block.blockhash(lastBlockNumber))来天生了hashVal这个参数,然则该变量的随机数种子是由区块已知的部份组成的。

我们布置合约并实行。

以太坊随机数平安周全剖析(二)

在log中看到了相干信息,一样我们可以在实行该函数前举行预支配以便可以展望随机数。

而我们在后面会对该范例的ctf问题举行一些简朴的报告。

三、将来区块的哈希值

末了的要领是运用将来区块哈希来举行随机数展望,要领步骤以下:

  • 玩家举行下注,合约对玩家的下注信息举行纪录并存在区块的编号

  • 第二步挪用合约,玩家讯问合约中奖号码的细致值

  • 合约从存储中检索保留的block.number并猎取其blockhash,然后用于天生伪随机数。

然则该要领一样存在一些弊病:

以太坊随机数平安周全剖析(二)

因为区块的扩大性问题,所以该要领不能保留一切的区块哈希,稚嫩保留近来的256个区块的哈希值,其他的值均为0.

所以当第二次挪用的值超过了256数目,此时哈希变成不可用,而且随机种子均为0 ,一样发生要挟。

被应用的这个缺点的最知名的案例是SmartBillions彩票的黑客进击。 合约对block.number的考证不充分,致使400 ETH丧失。

而在CTF中一样有相似的问题,比方本年强网杯的babybet问题就需要我们举行随机数展望。

function bet(var arg0) {
        var var0 = 0x00;
        memory[var0:var0 + 0x20] = msg.sender;
        memory[0x20:0x40] = var0;
        var var1 = var0;

        if (0x0a > storage[keccak256(memory[var1:var1 + 0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;

        if (0x02 <= storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + ~0x09;
        var0 = block.blockHash(block.number + ~0x00);
        var1 = var0 % 0x03;

        if (var1 != arg0) {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x01;
            storage[keccak256(memory[0x00:0x40])] = 0x02;
            return;
        } else {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x00;
            var temp1 = keccak256(memory[0x00:0x40]);
            storage[temp1] = storage[temp1] + 0x03e8;
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x01;
            storage[keccak256(memory[0x00:0x40])] = 0x02;
            return;
        }
    }

而我们写剧本便可以对其举行进击。

应用PHP函数parse_str绕过IDS、IPS和WAF

TL;DR 一般来说,PHP将查询字符串(在URL或主体中)转换为$_GET或$_POST内的关联数组。例如:/?foo=bar变为Array([foo] => “bar”)。查询字符串解析过程将参数名称中的某些字符删除或替换为下划线。例如/?%20news[id%00=42将转换为Array([news_id]=>42)。如果IDS/IPS或WAF在news_id参数中设置了阻止或记录非数字值的规则,则可以滥用此解析过程来绕过它:就像: /news.php?%20news[id%00=42″+AND+1=0– 在PHP中,上面的示例%20news[id%00中的参数名的值将存储为$_GET[“news_id”]。 原因 PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它主要做两件事: 删除初始空格 将一些字符转换为下划线(包括空格) 举个栗子 通过如下所示的循环,您可以使用parse_str函数发现哪个字符被删除或转换为下划线:

function process() public {
        target.profit();
        bytes32 guess = block.blockhash(block.number - 0x01);
        uint guess1 = uint(guess) % 0x03;
        target.bet(guess1);

    }

细致的题解可以看我之前的文章:

https://xz.aliyun.com/t/5372

四、私有种子作为哈希

为了增添随机数的随机性,一些合约运用了被以为“私有”的种子作为随机数天生的参数。比方:

bytes32 _a = block.blockhash(block.number - pointer);
for (uint i = 31; i >= 1; i--) {
  if ((uint8(_a[i]) >= 48) && (uint8(_a[i]) <= 57)) {
    return uint8(_a[i]) - 48;
  }
}

在该代码中,合约定义了pointer指针作为私有变量,合约建立者本意想隐蔽该私有变量,让用户参与者没法猎取到该种子的细致数字。在每次游戏以后,1到9之间的中奖号码被分配给该变量,然后在检索blockhash时将其用作当前block.number的偏移量。

然则我们晓得,区块链的规则是将数据公然通明,所以这些所谓的私有变量可以经由过程web3的要领举行猎取,下面我们对该要领举行细致的解说。

五、私有变量检察要领

检察合约制订内存的要领,虽然合约定义的内容是private范例,然则我们依然可以拿到。

文档概况:https://web3js.readthedocs.io/en/1.0/web3-eth.html

以太坊随机数平安周全剖析(二)

在合约中,我们可以如许举行检察:

pragma solidity ^0.4.18;

contract Vault {
  bool public locked;
  bytes32 private password;

  function Vault(bytes32 _password) public {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}

若要检察在内存中第一个位置的函数值,那末运用web3.eth.getStorageAt("0x80994df46e168050262d1a63129592fc6247b4ed", 1, function(x, y) {console.warn(web3.toAscii(y))});

以太坊随机数平安周全剖析(二)

运用这个函数是只可以检察private变量吗?

我们举行测试。

测试代码:

pragma solidity ^0.4.10;


contract attack{

    uint private a;

    uint public b;

    uint private c;

    bool public locked;
    bool private locked1;

    function attack(){
        a = 1;
        b = 2;
        c = 3;
        locked = true;
        locked1 = false;
    }

}

然后我们对一切的变量举行接见测试:

以太坊随机数平安周全剖析(二)

这里我们发明关于布尔变量,我们可以取得true为1,false为0 。

以太坊随机数平安周全剖析(二)

六、Front-running

为了取得最大嘉奖,矿工依据每笔生意业务运用的累计gas挑选生意业务来建立新区块。区块中生意业务实行的递次由gas价钱决议。gas价钱最高的生意业务将起首实行。因而,经由过程支配gas价钱,可以在当前区块中的一切其他生意业务之前实行希冀的生意业务。当合同的实行流程取决于其在一个区块中的位置时,这能够组成安全问题 – 一般称为Front-running。

彩票运用外部函数来取得伪随机数,用于肯定每轮投注中的投注者中的得胜者。这些号码是未加密的。进击者能够会视察待处置惩罚事件池并守候来自oracle的号码。一旦oracle的生意业务出现在生意业务池中,进击者就会以更高的汽油价钱下注。进击者的生意业务末了一轮,但因为天然气价钱较高,实际上是在oracle生意业务之前实行,使得进击者得胜。ZeroNights ICO Hacking Contest中有如许的使命。

这类问题一样存在于很多游戏合约中,比方Fome3D这类。很严重的伤害。

另一个例子是名为“Last is me!”的游戏。每当玩家购置一张票时,该玩家就会宣称末了一个坐位而且计时器最先倒计时。假如没有人在肯定数目的街区内购置机票,末了一个“坐下”的玩家将取得积累奖金。当该轮行将结束时,进击者可以视察生意业务池以举行其他参赛者的生意业务,并经由过程更高的gas价钱取得积累奖金。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明以太坊随机数平安周全剖析(二)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址