Delegatecall
Last updated
Last updated
Delegatecall and call are two ways to do external message calls to contracts
Delegatecall and call function almost identically, but delegatecall preserves the context of the caller.
Let's see the difference between them, firstly below is call diagram:
And here is a delegatecall diagram:
When we delegatecall to Target contract, the context is on Caller contract, all state change logics reflect on Caller’s storage.
In a nutshell, EOA that calls contract_a, which in hand calls contract_b with delegate call is basically like saying:
contract_a is allowing contract_b to change its state variables. Furthermore, when the delegatecall reaches contract_b, the msg.sender and origin is the EOA (preserved context).
From this, we can derive two important notes:
Delegatecall preserves context
Storage layout must be the same in order to properly use delegatecall
Delegatecall is usually used within the context of libraries to modularize code. When used incorrectly, it opens up a large security issue.
How EVM saves field variables to Storage? In Ethereum, there are two kinds of space for saving the contract’s field variables. One is “memory” and the other is “storage”.
For example if we have:
But why is that important?
In the case of delegatecall, the target contract accesses the same storage slots as the calling contract. And again, the order of the slots is defined by the order of the variables.
But what if we change the order so that it doesn’t align with the calling contract anymore?
Let's see the above with an example:
We have contracts HackMe and Lib:
As we discussed above, the order of variables matters when using delegatecall and in Lib this is not respected.
If doSomething in Lib is executed with the context of HackMe the first variable
will now be changed instead of someNumber Here is in detail what is going to happen:
lib.delegatecall(doSomething) is invoked
Due to the invokation of doSomething in contract Lib, now address public lib is changed
An attacker that is having the same variable structure is calling his changeOwner function in his Attack contract
This leads again to lib.delegatecall(doSomething) in HackMe which leads to doSomething in contract Lib
Now the address public lib in HackMe is changed to the address of the attacker(casted in number).
Immediately after that when initiate the next line of the changeOwner function, the function doSomething is invoked but this time this is the doSomething function of the Attack contract
Attack contract is now the owner
Let's see the full Attack contract: