Appearance
S1C7: Delegate
1. Problem
The challenge uses delegatecall to invoke a Delegate contract. You need to manipulate the target and parameters of the delegatecall to obtain the flag.
2. Root Cause
delegatecall executes the code of the called contract within the caller's storage context. If the Delegate contract has a function that modifies key storage variables, an attacker can modify the challenge contract's storage via delegatecall.
3. Solution
- Analyze the storage layout of the Delegate contract to determine which function modifies the challenge contract's critical variable
- Call that function through the challenge contract's
delegatecallentry point - Complete the attack with two
cast sendcalls
bash
# Step 1: Modify critical state via delegatecall
cast send $CHALLENGE "forwardCall(bytes)" <encoded_delegate_call>
# Step 2: Call mintFlag
cast send $CHALLENGE "mintFlag()"
4. Pitfalls Encountered
Storage layout collision: delegatecall preserves the caller's storage layout, so variables must occupy the same storage slots.
5. Why the Pitfall Exists
delegatecall does not change msg.sender or the storage context — it only borrows the called contract's code logic. Variables are stored in slots in declaration order. If two contracts declare variables in different orders, unexpected storage overwrites can occur.
6. How to Resolve
Carefully analyze the storage layouts of both the challenge contract and the Delegate contract. Use cast storage to read key slots and ensure the correct function is called.
7. Key Takeaways
| Point | Explanation |
|---|---|
| delegatecall | Borrows the target contract's code but uses the caller's storage context and msg.sender |
| Storage layout collision | A classic problem in contract upgrade/proxy patterns: storage slots must be aligned |
| Proxy pattern | delegatecall is the foundation of UUPS/Transparent proxy patterns |