Hey all, I was playing a bit with the Ethernaut CTF, and I think I understand something wrong with the delegatecall. (level 6). I solved it by sending the TX, but wanted to do it from a contract. As I want that the end call goes with msg.sender with my address, I used delegatecall like this:

(bool success, bytes memory result) = delegationAddress.delegatecall(abi.encodeWithSignature("pwn()"));

delegationAddress is a contract with a function like this:

fallback() external {
(bool result,) = address(delegate).delegatecall(msg.data);
if (result) {
this;
}

and delegate has a function like this:

function pwn() public {
owner = msg.sender;
}

If I am not wrong, if I call a function in my contract with that line, it should send the pwn() encoded call in the data (0xdd365b8b) to delegationAddress, which with the fallback function will delegate the call to pwn, which will receive my address and change the owner, but that does not work.

However, if I use call in my contract instead of delegatecall, the address of the contract is set as the owner as expected, so what I am doing wrong?

May 12, 2022, 10:45 AM
Are you checking data in the proxied contract or the delegate contract? If you use call, it will affect data in the delegate contract... if you use delegatecall, it will affect data in the proxied contract.
Also, I'm not even sure how you would check the data in the proxied contract unless the data member layouts were the same in each. I don't think you could check it easily with etherscan. I think you would probably need to use the abi from the delegate contract but attach it to the proxied contract address.
May 12, 2022, 10:59 AM
mmm why is that so? My understanding is that if I do a delegatecall from my contract calling the pwn() to the proxy, and the proxy do a delegatecall to the delegate contract, the delegatecontract would receive my EOA address in msg.sender, but it would change the storage of the proxy?
In this case: https://ethernaut.openzeppelin.com/level/0x9451961b7Aea1Df57bc20CC68D72f662241b5493 the "proxy" (Delegation contract) has the owner variable
so I can check that one
May 12, 2022, 11:01 AM
Ok... in this case Delegation is the contract being proxied and Delegate is the proxy (or delegate). So... if you use call, data in Delegate will change, but if you use delegatecall data in Delegation will change.
May 12, 2022, 11:04 AM
I think I understand my mistake now... you call to Delegation (with the fallback function), , when using call:

myContract -> call Delegation -> delegateCall Delegate, storage of Delegation is changed

and if using delegateCall:

myContract -> delegateCall Delegation -> delegateCall Delegate, storage of myContract is changed.

Would that be correct?
May 12, 2022, 11:07 AM
Hmm... I've never seen a double delegatecall so I'm not sure what would happen in the second case, but I would assume it to work exactly as you stated.
May 12, 2022, 11:09 AM
I basically focused on letting the pwn() function have my address in msg.sender, to be able to solve it with a contract (I know there is an easier way), but didn't think that much about the storage
thanks William for the help ;)
May 12, 2022, 11:13 AM

© 2024 Draquery.com All rights reserved.