Capture the ether-Guess secret number
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