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

#!/usr/bin/python3
from Crypto.Util.Padding import pad, unpad
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
from hashlib import sha256
import requests,json,sys,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.