Ethernaut Level 6 — Delegation
--
The Ethernaut is a Web3/Solidity based wargame inspired on overthewire.org. Here’s the solution to the Level 6 Delegation.
The challenge description states:
The goal of this level is for you to claim ownership of the instance you are given.Things that might help- Look into Solidity’s documentation on the delegatecall low level function, how it works, how it can be used to delegate operations to on-chain libraries, and what implications it has on execution scope.- Fallback methods- Method ids
The description heavily hints what needs to be done. Let’s look at the code. There are two contracts: delegation
and delegate
. The delegation
is the one deployed and contains a delegatecall
in the fallback function. The delegatecall
is unsafe, because because is called for user-suppplied data (from msg.data
):
A contract which is a target of the delegatecall
is called delegate
. It contains a funtion named pwn
, which sets the contract owner to a current sender. What’s important, its owner
property is at the same position, as the same property in the delegation
contract.
As it turns out, when the delegatecall
is run, it modifies caller’s data, not callee’s. In this case, the delegation
contract running the delegate’s pwn
function, will set it’s own owner to a sender. Which is all the attacker needs!
How to do it in practice? First I’ll need a 4-byte function call hash:
> callHash = web3.eth.abi.encodeFunctionCall({name:”pwn”, type: “function”, inputs:[]},[])‘0xdd365b8b’
Now I need to send a transaction to the delegation
contract to force the fallback
call. The call data will be the callHash I’ve calculated above to trigger the pwn
function:
> await web3.eth.sendTransaction({"to": instance, "from": player, "data": callHash})
That’s it! The contract has been take over, and the challenge can be submitted.