HTB Hack The Boo Practice: SPG
Challenge Information
| Attribute | Details |
|---|---|
| Event | Hack The Boo Practice |
| Category | Cryptography |
| Challenge | SPG |
| Difficulty | Very 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 passwordEach 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&#$@uEncrypted 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 AESfrom Crypto.Util.Padding import unpadfrom base64 import b64decodefrom hashlib import sha256
encrypted_flag = "tnP+MdNjHF1aMJVV/ciAYqQutsU8LyxVkJtVEf0J0T5j8Eu68AxcsKwd0NjY9CE+Be9e9FwSVF2xbK1GP53WSAaJuQaX/NC02D+v7S/yizQ="
# Convert reconstructed master key back to bytesmaster_key_bytes = reconstructed_master_key.to_bytes( (reconstructed_master_key.bit_length() + 7) // 8, 'big')
# Derive encryption keyencryption_key = sha256(master_key_bytes).digest()
# Decryptencoded_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