-
Notifications
You must be signed in to change notification settings - Fork 4
06. Delegate
Claim ownership of the contract.
The Delegation
fallback implements a delegatecall
.
By sending the right msg.data
we can trigger the function pwn()
of the Delegate contract.
Since this function is executed by a delegatecall
the context will be preserved:
owner = msg.sender = address of contract that send data to the Delegation fallback (attacker contract)
There are several ways to interact with other contracts from within a given contract.
If the ABI (like an API for smart contracts) and the contract's address are known, we can simply instantiate (e.g with a contract interface) the contract and call its functions.
contract Called {
function fun() public returns (bool);
}
contract Caller {
Called public called;
constructor (Called addr) public {
called = addr;
}
function call () {
called.fun();
}
}
Calling a function means injecting a specific context (arguments) to a group of commands (function) and commands are executing one by one with this context.
In Ethereum, a function call can be expressed by a 2 parts bytecode as long as 4 + 32 * N bytes.
- Function Selector: first 4 bytes of function call’s bytecode.
Generated by hashing target function’s name plus with the type of its arguments
excluding empty space. Ethereum uses keccak-256 hashing function to create function selectors:
functionSelectorHash = web3.utils.keccak256('func()')
- Function Argument: convert each value of arguments into a hex string padded to 32 bytes.
If there is more than one argument, they are concatenated.
In Solidity encoding the function selector together with the arguments can be done with globally available encoding/decoding functions: e.g.
abi.encodeWithSignature("add(uint256,uint256)", valueForArg1, valueForArg2)
Can be used to invoke public functions by sending data
in a transaction.
contractInstance.call(bytes4(keccak256("functionName(inputType)"))
DelegateCall: preserves context
contractInstance.delegatecall(bytes4(keccak256("functionName(inputType)"))
Delegate calls preserve current calling contract's context (storage, msg.sender, msg.value).
The calling contract using delegate calls allows the called contract to mutate its state.
- Compute the encoded hash that will be used for
msg.data
- Send
msg.data
in a transaction to the contract fallback
- Use the higher level
call()
function to inherit from libraries, especially when you don’t need to change contract storage and do not care about gas control. - When inheriting from a library intending to alter your contract’s storage, make sure to line up your storage slots with the library’s storage slots to avoid unexpected state changes..
- Authenticate and do conditional checks on functions that invoke
delegatecall
s.