HackTheBox Not that random Challenge
https://app.hackthebox.com/challenges/704
Description
Uncertain of their safety from other potentially hostile communities, the survivors recognize the necessity of arming themselves with laser weapons and flamethrowers for self-defense. Rumor has it that an old casino on the city’s outskirts holds advanced weaponry. However, to gain access to this private area, an uninvited visitor must play a seemingly impossible game of chance and accumulate a specific amount of winnings. Is the challenge as it appears, or can you prove them wrong?
Exploitation
#!/usr/bin/env python3
from pwn import *
from Crypto.Hash import SHA256
def get_process():
if len(sys.argv) > 1:
ip, port = sys.argv[1].split(':')
return remote(ip, int(port))
print(f"Usage: {sys.argv[0]} <ip:port>")
sys.exit(1)
def keyed_hash(key, inp):
return SHA256.new(key + inp).digest()
def custom_hmac(key, inp):
return keyed_hash(keyed_hash(key, b"Improving on the security of SHA is easy"), inp) + keyed_hash(key, inp)
def check_output(full_output, inp, k_prime):
output = bytes.fromhex(full_output)
return output[:32] == keyed_hash(k_prime, bytes.fromhex(inp))
def get_fixed_key(conn):
msg = b"Improving on the security of SHA is easy"
conn.recvline()
my_balance = 100
appeared = []
for i in range(10):
my_balance -= 10
conn.recvuntil(b"Option: ")
conn.sendline(b"2")
conn.recvuntil(b"hex :: ")
conn.sendline(msg.hex().encode())
potential_hash = conn.recvline().strip().decode().split()[-1]
appeared.append(potential_hash)
if appeared.count(appeared[-1]) > 1:
return bytes.fromhex(appeared[-1][64:]), my_balance
return None, my_balance
def win_game(conn, H_k_msg, my_balance):
while my_balance < 500:
conn.recvuntil(b"Option: ")
conn.sendline(b"3")
curr_input = conn.recvline().decode().strip().split()[-1]
curr_output = conn.recvline().decode().strip().split()[-1]
conn.recvuntil(b" :: ")
conn.sendline(b"0" if check_output(curr_output, curr_input, H_k_msg) else b"1")
my_balance += 5 if "Lucky" in conn.recvline().decode() else 0
def get_flag(conn):
conn.recvuntil(b"Option: ")
conn.sendline(b"1")
return conn.recvline().decode().strip()
def pwn():
try:
conn = get_process()
if not conn: return
H_k_msg, my_balance = get_fixed_key(conn)
if not H_k_msg: return
win_game(conn, H_k_msg, my_balance)
print(get_flag(conn))
except Exception as e:
print(f"Error: {str(e)}")
finally:
if conn: conn.close()
if __name__ == '__main__':
pwn()
Summary
Not that random: reconstruct the PRNG state from the leak, replay it, and recover the flag.