Delegation
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Delegate {
address public owner;
constructor(address _owner) {
owner = _owner;
}
function pwn() public {
owner = msg.sender;
}
}
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;
}
}
}
Goal
The goal of this level is to claim ownership of the Delegation instance.
Exploit
In order to exploit this contract one must first understand how fallback
methods and delegatecall
low function works.
fallback
is a special function that is executed when a function that does not exist is called from some smart contract.
delegatecall
is a low level function similar to call, with the only difference when we make a delegatecall to some smart contract, the callee's smart contract code is executed in context of the caller (this means the caller storage is changed, and msg.sender and msg.value from the caller are taken into account).
In our case, when we try to call the pwn()
function inside Delegation
contract, since this function is not present in the Delegation
contract, its fallback
function will be triggered and it will delegatecall to the Delegate
contract with the pwn()
function funcion-selector argument for msg.data.
To exploit the contract we need to:
- copy the code from Ethernaut to Remix
- get the contract instance address from the web console
- load the
Delegate.sol
contract at the contract instance address (it is important to loadDelegate.sol
contract and notDelegation.sol
) - call the
pwn()
function inside Remix. NOTE: we need to increase gas limit in MetaMask in order for the transaction to succeed