Delegate call hijacking
Excavation initiation!
今日讲讲入门漏洞–delegatecall 劫持
这是一个非常有代表性的陷阱式漏洞,攻击者不需要合约授权、不需要提前部署攻击逻辑,只要不小心用了 delegatecall + 外部地址,就会把你合约的状态变量拱手让人。
什么是 delegatecall?
简单说:
delegatecall是一种 Solidity 中的底层调用方式,用来执行其他合约的代码,但使用自己合约的storage layout。
官方定义是:
A.delegatecall(B)会在 A 的上下文中执行 B 的函数,B 中对变量的任何修改,都会作用在 A 上。
为啥 delegatecall 会出事?
在以下情况,使用 delegatecall 就极容易出事:
- 调用了一个“库合约”来做逻辑抽象(这儿没问题);
- 把库地址保存在状态变量里,攻击者可以改这个地址,换成自己的恶意合约;
- 没有校验
delegatecall的来源,也没限制调用者; - 把库和主合约的storage布局搞错了(比如库没
owner,主合约有owner,就可能被覆盖),那这时问题就更严重了。
示例合约
Lib 合约:一个看起来人畜无害的逻辑库
1 | contract Lib { |
只做了一件事,把 _num 写进 someNumber,本身没问题。
HackMe 合约:受害者人
1 | contract HackMe { |
问题来了:
delegatecall调用Lib.UpdateSomething();UpdateSomething()会写入someNumber,但它是在Lib的 slot 0;- 在
HackMe里,slot 0 是lib,slot 1 是owner。
所以攻击者只要构造一个伪装成库的合约,就能用 UpdateSomething() 覆盖 owner、替代库地址、甚至后门写入任意状态变量
攻击合约
1 | contract exploit { |
攻击流程:
- 攻击者部署
exploit合约; - 调用
attack():- 第一次
UpdateSomething()把lib地址改为exploit; - 第二次
UpdateSomething()实际 delegatecall 到攻击者自己的函数; - 改写
owner成为msg.sender(攻击者);
- 第一次
- 接管整个合约
PWN手DNA动了,你长得好像GOT表函数指针劫持,似故人三分
如何防御
有几个方向可以修复这种问题:
1. 把owner,库函数地址之类的变量声明为immutable
2. 加权限控制
1 | require(msg.sender == owner) |
3. 直接别把库函数地址作为状态变量(使用 using for)
避免写成 delegatecall(lib, ... ),而是用 internal library 或 using LibraryName for Type 方式调用。
4. 使用固定的逻辑合约 + proxy 模式 + 可升级框架
如果一定要动态调用地址,那最好:
- 确保逻辑合约和存储合约布局完全一致;
- 禁止随意调用敏感逻辑函数;
- 或使用 OpenZeppelin 的
TransparentProxy框架自动处理 delegatecall 安全。
- Title: Delegate call hijacking
- Author: Chiu
- Created at : 2025-04-22 21:52:31
- Updated at : 2025-07-15 13:01:43
- Link: https://github.com/Idealist17/github.io/2025/04/22/delegateCall—hijack/
- License: This work is licensed under CC BY-NC-SA 4.0.
Comments