HackTheBox Hybrid Unifier Writeup
Explore the basics of cybersecurity in the Hybrid Unifier Challenge on Hack The Box. This easy-level Challenge introduces encryption reversal and file handling concepts in a clear and accessible way, perfect for beginners.
https://app.hackthebox.com/challenges/796
Description
In the depths of an ancient library, an old manuscript held the key to an unseen power. Scholars who dared to unlock its secrets would first exchange a series of encrypted symbols, forming a bond no one could break. As they secured their connection, layers of protection wrapped around them like invisible chains. But as the final cipher was set, a chilling realization struck—the connection they forged was now bound to something far darker, something watching from the shadows.
Exploitation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from Crypto.Util.Padding import pad, unpad
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
from hashlib import sha256
import requests
import json
import sys
import os
def get_base_url():
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <ip:port>")
sys.exit(1)
host, port = sys.argv[1].split(':')
return f"http://{host}:{port}/api"
BASE_URL = get_base_url()
def generate_keypair(g, p):
a = int.from_bytes(os.urandom(48), 'big') % (p - 1) + 1
A = pow(g, a, p)
return a, A
def init_session(client_public_key):
payload = {"client_public_key": int(client_public_key)}
response = requests.post(f"{BASE_URL}/init-session", json=payload)
if response.status_code == 200:
data = response.json()
server_public_key = int(data['server_public_key'], 16)
return server_public_key
else:
print("Error in init_session:", response.status_code, response.text)
return None
def compute_session_key(a, server_public_key, p):
shared_secret = pow(server_public_key, a, p)
return sha256(str(shared_secret).encode()).digest()
def decrypt_challenge(session_key, encrypted_challenge):
encrypted_data = b64decode(encrypted_challenge)
iv, encrypted = encrypted_data[:16], encrypted_data[16:]
cipher = AES.new(session_key, AES.MODE_CBC, iv)
return unpad(cipher.decrypt(encrypted), AES.block_size)
def get_dh_params():
response = requests.post(f"{BASE_URL}/request-session-parameters")
if response.status_code == 200:
params = response.json()
return int(params['g'], 16), int(params['p'], 16)
else:
print("Error fetching DH parameters:", response.status_code, response.text)
return None, None
def main():
print("[+] Requesting DH parameters...")
g, p = get_dh_params()
if g is None or p is None:
print("[-] Failed to get DH parameters")
return
print(f"[+] Received g: {g}, p: {p}")
print("[+] Generating keypair...")
a, client_public_key = generate_keypair(g, p)
print("[+] Initializing session...")
server_public_key = init_session(client_public_key)
if server_public_key is None:
print("[-] Failed to initialize session")
return
print(f"[+] Received server public key: {server_public_key}")
print("[+] Computing session key...")
session_key = compute_session_key(a, server_public_key, p)
print("[+] Requesting challenge...")
response = requests.post(f"{BASE_URL}/request-challenge")
if response.status_code != 200:
print("[-] Failed to get challenge:", response.status_code, response.text)
return
encrypted_challenge = response.json()['encrypted_challenge']
challenge = decrypt_challenge(session_key, encrypted_challenge)
challenge_hash = sha256(challenge).hexdigest()
print("[+] Challenge decrypted and hashed")
print("[+] Requesting flag...")
iv = os.urandom(16)
cipher = AES.new(session_key, AES.MODE_CBC, iv)
encrypted_packet = iv + cipher.encrypt(pad(b'flag', 16))
packet_data = b64encode(encrypted_packet).decode()
response = requests.post(f"{BASE_URL}/dashboard", json={"challenge": challenge_hash, "packet_data": packet_data})
if response.status_code == 200:
encrypted_flag = b64decode(response.json()['packet_data'])
flag = decrypt_challenge(session_key, response.json()['packet_data'])
print("[+] Flag:", flag.decode())
else:
print("[-] Error during flag retrieval:", response.status_code, response.text)
if __name__ == "__main__":
main()
Summary
Hybrid Unifier employs Diffie-Hellman for generating a shared session key and AES-CBC encryption for securely exchanging data. The client and server negotiate a shared key, which the client then uses to decrypt a challenge, hash the result, and respond. Upon successful authentication, the server provides an encrypted flag, which the client decrypts using the shared session key, revealing the flag.