Capture the ether-Guess secret number

problem link

pragma solidity ^0.4.21;
 
contract GuessTheSecretNumberChallenge {
    bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;
 
    function GuessTheSecretNumberChallenge() 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);
 
        if (keccak256(n) == answerHash) {
            msg.sender.transfer(2 ether);
        }
    }
}

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

풀이

    function guess(uint8 n) public payable {
        require(msg.value == 1 ether);
 
        if (keccak256(n) == answerHash) {
            msg.sender.transfer(2 ether);
        }
    }

guess 함수에서 8비트짜리 수를 받아 answerHash와 비교한다.

answerHash는 bytes32 answerHash = 0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365;로 하드코딩되어 있다.

keccak256는 단순히 해시이므로 외부에서도 계산해볼 수 있다. 8비트 범위인 0~255를 bruteforce하여 찾아낸다.

import { Contract, ethers } from "ethers"
import 'dotenv/config'
 
const provider = new ethers.providers.getDefaultProvider("ropsten")
const wallet = new ethers.Wallet(process.env.PK, provider)
 
const abi = [{"constant":false,"inputs":[{"name":"n","type":"uint8"}],"name":"guess","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"isComplete","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":true,"stateMutability":"payable","type":"constructor"}]
const problem_address = ""
 
async function solve() {
    // bruteforce
    for(let i = 0; i < 256; i++){
        if(ethers.utils.keccak256(i) === "0xdb81b4d58595fbbbb592d3661a34cdca14d7ab379441400cbfa1b78bc447c365"){
            console.log(i)
 
            const contract = new Contract(problem_address, abi, wallet)
            const tx = await contract.guess(i, {
                value: ethers.utils.parseUnits("1", "ether")
            })
            await tx.wait()
            
            break;
        }
    }
}
 
solve().catch((err) => {
    console.log(err)
})

다음은 공격 코드이다.


tags: writeup, blockchain, solidity, smart contract, bruteforce