Ethernaut: 6. 위임

Play the level

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Delegate {
  address public owner;

  constructor(address _owner) public {
    owner = _owner;
  }

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

contract Delegation {
  address public owner;
  Delegate delegate;

  constructor(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);
    owner = msg.sender;
  }

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

delegatecall는 중요한 함수입니다. 일반적으로 컨트랙트는 message calls을 만들어 기능을 호출합니다. delegatecall 은 기본적으로 계약의 컨텍스트를 다른 계약으로 전달하고 원하는 대로 수행하도록 하는 보다 전문적인 호출입니다. 이는 스토리지 변수에서 작동할 수 있는 라이브러리를 구현하거나 프록시가 시간이 지남에 따라 다양한 계약에 대리자를 호출하는 업그레이드 가능한 계약이 있는 경우에 유용합니다.
delegatecall 동안 다음은 변경되지 않습니다.
  • msg.sender
  • msg.value
  • address(this)
  • 스토리지 레이아웃(이 챌린지에서 활용)

  • 대리인 호출이 실제로 잘 작동하는 방식을 설명하는 이 문서를 참조하고 싶습니다. https://eip2535diamonds.substack.com/p/understanding-delegatecall-and-how?s=r .

    이 예의 공격은 하나의 트랜잭션일 뿐입니다.

    await sendTransaction({
        from: player,
        to: contract.address,
        data: "0xdd365b8b"
    })
    


    이제 data 부분을 살펴보겠습니다. EVM은 함수 서명의 처음 4바이트를 보고 함수를 호출합니다. 함수 시그니처는 함수 프로토타입의 keccak256(즉, sha3 )입니다. 이 경우 web3.utils.sha3('pwn()').slice(2, 2 + 4 * 2)dd365b8b를 제공합니다. 함수 매개변수가 있다면 각각 32바이트로 주겠지만 이 경우에는 매개변수가 없기 때문에 함수 서명만 데이터로 씁니다.

    이를 가지고 Delegation Contract를 호출하면 Fallback 기능으로 넘어갑니다. 거기에서 msg.data를 매개변수로 하는 delegatecall이 이루어지므로 Delegate의 pwn 함수를 호출하게 됩니다.

    실제 익스플로잇은 스토리지와 관련이 있습니다. 두 계약 모두 스토리지의 첫 번째 슬롯에 address public owner가 있습니다. delegatecall 를 사용하면 호출자의 저장소가 활성화되고 호출 수신자가 슬롯과 관련하여 이를 업데이트할 수 있습니다. 보시다시피 pwn 업데이트 owner 이것은 사실상 동일한 슬롯에서 호출자의 스토리지 값을 업데이트하며 이는 다시 소유자 주소입니다.

    따라서 pwn 내의 저장 변수 할당은 delegatecall를 만든 계약에 영향을 미치고 우리는 소유자가 됩니다.

    좋은 웹페이지 즐겨찾기