HTB Hack The Boo Practice: SPG

Challenge Information

AttributeDetails
EventHack The Boo Practice
CategoryCryptography
ChallengeSPG
DifficultyVery Easy

Summary

After successfully joining the Applied Cryptography Academy of Ghosts, there is an eclass login system. The challenge provides a password generator script that needs to be reverse-engineered. By analyzing the password generation algorithm and extracting the master key, students can decrypt the encrypted flag using AES encryption.


Analysis

The password generator uses a custom algorithm that converts bits from a master key into characters from an alphabet. The algorithm works as follows:

def generate_password():
master_key = int.from_bytes(MASTER_KEY, 'little')
password = ''
while master_key:
bit = master_key & 1
if bit:
password += random.choice(ALPHABET[:len(ALPHABET)//2])
else:
password += random.choice(ALPHABET[len(ALPHABET)//2:])
master_key >>= 1
return password

Each bit of the master key determines which half of the alphabet to sample from. The alphabet is split in half:

  • Bits = 1: First half (uppercase/lowercase letters)
  • Bits = 0: Second half (digits and special characters)

The encrypted flag is encrypted using:

encryption_key = sha256(MASTER_KEY).digest()
cipher = AES.new(encryption_key, AES.MODE_ECB)

Challenge Output:

Your Password : gBv#3%DXMV*7oCN2M71Zfe0QY^dS3ji7DgHxx2bNRCSoRPlVRRX*bwLO5eM&0AIOa&#$@u
Encrypted Flag : tnP+MdNjHF1aMJVV/ciAYqQutsU8LyxVkJtVEf0J0T5j8Eu68AxcsKwd0NjY9CE+Be9e9FwSVF2xbK1GP53WSAaJuQaX/NC02D+v7S/yizQ=

Solution

Step 1: Reconstruct the MASTER_KEY from the password

The password can be reversed to reconstruct the bits of the master key:

import string
ALPHABET = string.ascii_letters + string.digits + '~!@#$%^&*'
def reverse_password_generation(password):
reconstructed_master_key = 0
for char in password:
half_index = len(ALPHABET) // 2
if char in ALPHABET[:half_index]:
# First half = bit 1
reconstructed_master_key = (reconstructed_master_key << 1) + 1
else:
# Second half = bit 0
reconstructed_master_key = (reconstructed_master_key << 1)
return reconstructed_master_key
password_from_output = "gBv#3%DXMV*7oCN2M71Zfe0QY^dS3ji7DgHxx2bNRCSoRPlVRRX*bwLO5eM&0AIOa&#$@u"
master_key = reverse_password_generation(password_from_output)

Step 2: Derive the encryption key and decrypt the flag

Once the master key is reconstructed:

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from base64 import b64decode
from hashlib import sha256
encrypted_flag = "tnP+MdNjHF1aMJVV/ciAYqQutsU8LyxVkJtVEf0J0T5j8Eu68AxcsKwd0NjY9CE+Be9e9FwSVF2xbK1GP53WSAaJuQaX/NC02D+v7S/yizQ="
# Convert reconstructed master key back to bytes
master_key_bytes = reconstructed_master_key.to_bytes(
(reconstructed_master_key.bit_length() + 7) // 8, 'big'
)
# Derive encryption key
encryption_key = sha256(master_key_bytes).digest()
# Decrypt
encoded_flag = b64decode(encrypted_flag)
cipher = AES.new(encryption_key, AES.MODE_ECB)
decrypted_flag = unpad(cipher.decrypt(encoded_flag), AES.block_size)
print(f'Decrypted Flag: {decrypted_flag.decode()}')

Key Takeaways

  • Custom password generation algorithms can be reverse-engineered if the logic is known
  • Bit manipulation patterns leak information about the underlying key material
  • Never use random choice with a predictable key schedule
  • AES with ECB mode is deterministic - identical plaintexts produce identical ciphertexts
  • The security of AES depends on the unpredictability of the key, not the algorithm