1. 문제

king의 자리를 탈환하고, 다른 유저가 king의 자리를 뺏지 못하도록 해라
pragma solidity ^0.8.0;

contract King {
    address king;
    uint256 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;
    }
}

2. 문제 분석

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

receive() 가 동작하게 되면 이더를 기존 왕에게 전송하고 새로운 왕이 등록되는 방식이다

처음 문제를 생각했을 떄 “msg.value에 max값을 넣어 보내면 되지않을까?” 라는 생각을 하고 문제를 풀었는데, king을 탈환하는데는 성공했지만 msg.value >= prize 조건에 의해 다른 사람이 max 값을 보내면 똑같이 탈환당할 수 있었음..

흠…. 어캐 풀지????


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

0.01이더 보다 큰 금액을 전송하여 king 권한을 탈취한 후, receive() 함수가 동작할 떄 transfer 하는 과정에서 revert가 발생하면 king 권한 탈취를 막을 수 있다.

transfer 함수를 이용하여 이더를 전송할 떄 전송 받는 곳에서 receive or fallback 이 있어야 한다고 우리는 알고 있다. 이 점을 이용해 Attack 코드에 receive or fallback 코드를 넣지 않으면 transfer 하는 과정에서 자연스럽게 revert가 발생하여 다음 로직을 수행하지 않게 된다.

이를 이용 ㄱㄱㄱㄱ