Appearance
S1C7: Delegate
1. 问题
挑战使用 delegatecall 调用一个 Delegate 合约。需要操纵 delegatecall 的目标和参数来获取 flag。
2. 原因
delegatecall 在调用者的存储上下文中执行被调用合约的代码。如果 Delegate 合约有修改关键存储变量的函数,攻击者可以通过 delegatecall 修改挑战合约的存储。
3. 方案
- 分析 Delegate 合约的存储布局,确定哪个函数会修改挑战合约的关键变量
- 通过挑战合约的 delegatecall 入口调用该函数
- 两次
cast send完成攻击
bash
# Step 1: 通过 delegatecall 修改关键状态
cast send $CHALLENGE "forwardCall(bytes)" <encoded_delegate_call>
# Step 2: 调用 mintFlag
cast send $CHALLENGE "mintFlag()"
4. 遇到的陷阱
存储布局冲突: delegatecall 保持调用者的存储布局,需要使用相同存储槽位的变量。
5. 陷阱的原因
delegatecall 不改变 msg.sender 和存储上下文,只借用被调用合约的代码逻辑。变量按照声明顺序存储在槽位中,如果两个合约的变量声明顺序不同,会导致意外的存储覆盖。
6. 如何解决
仔细分析挑战合约和 Delegate 合约的存储布局。使用 cast storage 读取关键槽位,确保调用正确的函数。
7. 技术要点
| 要点 | 说明 |
|---|---|
| delegatecall | 借用目标合约的代码,但使用调用者的存储上下文和 msg.sender |
| 存储布局冲突 | 合约升级/代理模式中的经典问题:存储槽位必须对齐 |
| 代理模式 | delegatecall 是 UUPS/Transparent 代理的基础 |