当前位置:网站首页>智能合约安全审计入门篇 —— delegatecall (2)
智能合约安全审计入门篇 —— delegatecall (2)
2022-06-24 18:34:00 【慢雾科技】
By:小白@慢雾安全团队
背景概述
上篇文章中我们了解了什么是 delegatecall 函数以及一个基础的漏洞,这篇文章的目的是加深一下大家对 delegatecall 的印象并带大家一起去玩点刺激的,拿下一个进阶版的漏洞合约。
前置知识
这里就不再重复之前的基础知识了,不了解或者遗忘的可以再看看上一篇文章:《智能合约安全审计入门篇 —— delegatecall (1)》。
漏洞示例
contract Lib { uint public someNumber; function doSomething(uint _num) public { someNumber = _num; }}contract HackMe { address public lib; address public owner; uint public someNumber; constructor(address _lib) { lib = _lib; owner = msg.sender; } function doSomething(uint _num) public { lib.delegatecall(abi.encodeWithSignature("doSomething(uint256)", _num)); }}漏洞分析
这次的攻击目标依然是获得 HackMe 合约中的 owner 权限,我们可以看到两个合约中除了 HackMe 合约中的构造函数可以修改合约的 owner 其他地方并没有修改 owner 的函数。我们要如何完成攻击呢?这里需要一点小技巧,大家可以思考一下,刚好也可以验证一下自己对于之前知识的掌握程度以及自己的思维是否活跃。
是否有想法呢?没有想法也没关系,我们一起来看攻击是如何完成的:
攻击合约
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract Attack { // Make sure the storage layout is the same as HackMe // This will allow us to correctly update the state variables address public lib; address public owner; uint public someNumber; HackMe public hackMe; constructor(HackMe _hackMe) { hackMe = HackMe(_hackMe); } function attack() public { // override address of lib hackMe.doSomething(uint(uint160(address(this)))); // pass any number as input, the function doSomething() below will // be called hackMe.doSomething(1); } // function signature must match HackMe.doSomething() function doSomething(uint _num) public { owner = msg.sender; }}我们先看攻击流程:
- Alice 部署 Lib 合约;
- Alice 部署 HackMe 合约并在构造函数中传入 Lib 合约的地址;
- 攻击者 Eve 部署 Attack 合约并在构造函数中传入 HackMe 合约的地址;
- 攻击者调用 Attack.attack() 函数将 HackMe 合约中的 owner 变为自己。
咋回事儿呢?其实这个攻击方式就是很巧妙的运用了 delegatecall 这个函数修改 storage 类型变量时的特征:delegatecall 函数的执行环境是调用者的环境并且对于 storage 类型变量的修改是根据被调用合约变量存储的插槽位置来修改的。
- Attack.attack() 函数先将自己的地址转换为 uint256 类型(这一步是为了兼容目标合约中的数据类型)第一次调用 HackMe.doSomething() 函数;
- HackMe.doSomething() 函数使用 delegatecall 函数带着传入的 Attack 合约的地址调用了 Lib.doSomething() 函数;
- 可以看到 Lib.doSomething() 函数将合约中存储位置为 slot0 的参数改为传入的值,这样当 HackMe 合约使用 delegatecall 调用 Lib.doSomething() 函数时也将改变自己在 slot0 位置存储的变量的值,也就是将 lib 参数(这里存储的是 Lib 合约的地址)改为我们传入的 Attack 合约的地址。此时之前在 HackMe.lib 参数中存储的 Lib 合约的地址就被修改成我们传入的 Attack 合约的地址了;
- Attack.attack() 函数再次调用 HackMe.doSomething() 函数,由于在上一步我们已经将 HackMe.lib 变量修改为 Attack 合约的地址了,这时 HackMe.doSomething() 函数将不再调用之前的 Lib 合约而是用 delegatecall 去调用 Attack.doSomething() 函数。此时我们再来观察 Attack 合约的写法,发现其变量的存储位置故意和 HackMe 合约保持一致,并且不难发现 Attack.doSomething() 函数的内容也被攻击者写为 owner = msg.sender,这个操作修改了合约中存储位置为 slot1 的变量。所以 HackMe 合约使用 delegatecall 调用 Attack.doSomething() 函数就会将合约中存储位置为 slot1 的变量 owner 修改为 msg.sender 也就是 Eve 的地址,至此攻击者完成了他的攻击。
修复建议
作为开发者
- 在使用 delegatecall 时应注意被调用合约的地址不能是可控的;
- 在较为复杂的合约环境下需要注意变量的声明顺序以及存储位置。因为使用 delegatecall 进行外部调用时会根据被调用合约的数据结构来修改本合约相应 slot 中存储的数据,当数据结构发生变化时这可能会造成非预期的变量覆盖。
作为审计者
- 在审计过程中遇到合约中有使用 delegatecall 时需要注意被调用的合约地址是否可控;
- 当被调用合约中的函数存在修改 storage 变量的情况时需要注意变量存储插槽的位置,避免由于数据结构不一致而导致本合约中存储的 storage 变量被错误的覆盖。
注:本文参考于《Solidity by Example》
参考链接:
https://solidity-by-example.org/hacks/delegatecall
边栏推荐
- The mixed calculation of rpx and PX in JS by the uniapp applet
- Recommend a distributed JVM monitoring tool, which is very practical!
- Why are life science enterprises on the cloud in succession?
- Sr-gnn shift robot gnns: overlapping the limitations of localized graph training data
- R language Quantitative Ecology redundancy analysis RDA analysis plant diversity species data visualization
- Uniapp wechat applet calls mobile map to navigate to the target point
- 2022 network security C module of the secondary vocational group scans the script of the surviving target aircraft (municipal, provincial and national)
- Volcano becomes spark default batch scheduler
- Analysis on the issue of raising the right of MSSQL in 2021 secondary vocational group network security competition in Shandong Province
- Three layer switching experiment
猜你喜欢

Mcu-08 interrupt system and external interrupt application

Microservice system design -- data model and system architecture design

Wechat applet to realize stacked rotation
![subject may not be empty [subject-empty]](/img/6b/9b57a7ed3ab086036cb6dfe0b31de4.png)
subject may not be empty [subject-empty]

This is not safe

Volcano成Spark默認batch調度器

为什么 Nodejs 这么快?

Window object

How to use Fisher's least significant difference (LSD) in R

Architecture decryption from distributed to microservice: several common microservice architecture schemes
随机推荐
Air pollution gas satellite data download tutorial
Restful design method
解决执行MapReduce程序控制台没有日志信息WARN Please initialize the log4j system properly
717.1-bit and 2-bit characters [sliding window]
C self learning function
variable
Make track map
Navigator object
JS deep understanding of scope
微服务系统设计——子服务项目构建
How about China Power Investment Xianrong futures? Is it safe to open futures accounts?
okcc呼叫中心数据操作的效率问题
The mixed calculation of rpx and PX in JS by the uniapp applet
Self taught C special data type
How MySQL works - Chapter 14
three.js创建的基础框架
25.sql statement differentiation
Selection (030) - what is the output of the following code?
中电投先融期货这家公司怎么样?期货开户办理安全吗?
R中的指数回归