Ethernaut Challenge #4 솔루션 — 전화

이것은 "Let's play OpenZeppelin Ethernaut CTF" 시리즈의 4부입니다. 각 문제를 해결하는 방법을 설명하겠습니다.

The Ethernaut is a Web3/Solidity based wargame created by OpenZeppelin.
Each level is a smart contract that needs to be 'hacked'. The game acts both as a tool for those interested in learning ethereum, and as a way to catalogue historical hacks in levels. Levels can be infinite and the game does not require to be played in any particular order.



도전 #4: 전화



Claim ownership of the contract below to complete this level.

Level author: Kyle Riley



이 챌린지의 최종 목표는 계약의 소유권을 주장할 수 있는 것입니다.

계약 연구


Telephone 계약은 매우 작기 때문에 읽고 문제를 해결하는 방법을 이해하는 것이 빠를 것입니다.
owner 상태 변수는 constructor 에서 초기화됩니다. owner를 업데이트하는 유일한 함수는 changeOwner입니다.
function changeOwner(address _owner) public
하나의 매개변수만 사용하는 공용 함수입니다address _owner.tx.origin 값이 msg.sender와 다른 경우 함수 입력 매개변수owner_owner를 업데이트합니다.

이 문제를 해결하려면 msg.sendertx.origin 가 무엇인지 이해해야 합니다.

Solidity 공식 문서의 문서 페이지Block and Transaction Properties를 보면 다음 정의를 찾을 수 있습니다.
  • tx.origin ( address ): 트랜잭션 발신자(전체 콜 체인)
  • msg.sender ( address ): 메시지 발신자(현재 통화)
  • tx.originmsg.sender는 모두 글로벌 네임스페이스에 항상 존재하는 "특수 변수"로 주로 블록체인에 대한 정보를 제공하거나 범용 유틸리티 기능으로 사용됩니다.

    그러나 우리는 다음 사항에 주의를 기울여야 합니다.
  • msgmsg.sender를 포함하여 msg.value의 모든 구성원 값은 모든 외부 함수 호출에 대해 변경될 수 있습니다. 여기에는 라이브러리 함수에 대한 호출이 포함됩니다.
  • tx.origin는 트랜잭션을 보낸(원본에서) 주소를 반환하고 msg.senderexternal 호출을 시작한 값을 반환합니다.

  • 이것은 무엇을 의미 하는가?

    예를 들어 두 값의 서로 다른 값을 살펴보겠습니다.

    시나리오 A: Alice(EOA)가 직접 호출Telephone.changeOwner(Bob)
  • tx.origin : 앨리스 주소
  • msg.sender : 앨리스 주소

  • 시나리오 B: Alice(EOA)는 Forwarder.forwardChangeOwnerRequest(Bob)를 호출할 스마트 계약Telephone.changeOwner(Bob)을 호출합니다.

    인사이드Forwarder.forwardChangeOwnerRequest
  • tx.origin : 앨리스 주소
  • msg.sender : 앨리스 주소

  • 인사이드Telephone.changeOwner(Bob)
  • tx.origin : 앨리스 주소
  • msg.sender : 포워더(계약) 주소

  • 이것은 tx.origin가 항상 트랜잭션을 생성한 주소를 반환하고 msg.sender가 마지막 외부 호출을 한 주소를 반환하기 때문에 발생합니다.

    솔루션 코드


    Telephone 계약에 대한 호출 중간에 있을 계약을 생성하기만 하면 됩니다.

    contract Exploiter {
        function exploit(Telephone level) public {
            level.changeOwner(msg.sender);
        }
    }
    


    솔루션 코드에서 배포하고 호출하기만 하면 됩니다.

    function exploitLevel() internal override {
        vm.startPrank(player, player);
    
        Exploiter exploiter = new Exploiter();
    
        vm.stopPrank();
    }
    


    이전 블로그 게시물을 주의 깊게 살펴보셨다면 이미 치트 코드startPrank를 보셨을 것입니다. startPrank에는 또 다른 오버로드 버전이 있습니다.

    // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called
    function startPrank(address) external;
    
    // Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input
    function startPrank(address, address) external;
    


    이 경우에는 tx.orgin가 될 이니셜address(this)도 재정의해야 하기 때문에 두 번째 것을 사용하고 있습니다. 즉, 테스트 계약 자체의 주소입니다!

    챌린지 오프닝의 전체 솔루션을 읽을 수 있습니다Telephone.t.sol.

    추가 자료


  • Block and Transaction Properties
  • SWC-115: Authorization through tx.origin

  • Solidity 문서
  • Security Consideration about tx.origin
  • Consensys Ethereum Smart Contract Best Practices: tx.origin
  • Vitalik Buterin: Do NOT assume that tx.origin will continue to be usable or meaningful

  • 부인 성명



    이 리포지토리의 모든 Solidity 코드, 관행 및 패턴은 DAMN VULNERABLE이며 교육 목적으로만 사용됩니다.

    나는 어떠한 보증도 하지 않으며 이 코드베이스의 사용으로 인해 발생하는 손실에 대해 책임을 지지 않습니다.

    생산에 사용하지 마십시오.

    좋은 웹페이지 즐겨찾기