HackTheBox Initialization Challenge Writeup | Cryptography CTF Challenges
Introduction
This post covers a cryptographic HackTheBox Initialization (CTF) challenge that uses Python for encrypting messages with AES in CTR mode. The challenge demonstrates a security flaw caused by repeated key use, allowing cipher stream reuse across messages. This vulnerability permits decryption by XOR-ing ciphertext and known plaintext values. The walkthrough includes Python code to exploit this flaw, recover the cipher stream, and reveal the encrypted flag.
HackTheBox Initialization Description
During a cyber security audit of your government's infrastructure, you discover log entries showing traffic directed towards an IP address within the enemy territory of "Oumara". This alarming revelation triggers suspicion of a mole within Lusons' government. Determined to unveil the truth, you analyze the encryption scheme with the goal of breaking it and decrypting the suspicious communication. Your objective is to extract vital information and gather intelligence, ultimately protecting your nation from potential threats.
Source Code Analysis
Initially, the script retrieves messages from messages.txt and saves them in MSG. Afterward, it executes main
Within main, the script creates an instance of AdvancedEncryption, encrypts each message in MSG, and writes the encrypted results to output.txt.
The encryption method used in AdvancedEncryption is simply AES in CTR mode.
Since this mode operates as a stream cipher, the encryption algorithm simply XORs the plaintext with a cipher stream generated by AES in CTR mode.
#!/usr/bin/env python3
import os
from Crypto.Util import Counter
from Crypto.Util.Padding import pad
from Crypto.Cipher import AESclass AdvancedEncryption:
def __init__(self, block_size):
self.KEYS = self.generate_encryption_keys()
self.CTRs = [Counter.new(block_size) for i in range(len(MSG))] # nonce reuse : avoided! def generate_encryption_keys(self):
keys = [[b'\x00']*16] * len(MSG)
for i in range(len(keys)):
for j in range(len(keys[i])):
keys[i][j] = os.urandom(1)
return keys
def encrypt(self, i, msg):
key = b''.join(self.KEYS[i])
ctr = self.CTRs[i]
cipher = AES.new(key, AES.MODE_CTR, counter=ctr)
return cipher.encrypt(pad(msg.encode(), 16))def main():
AE = AdvancedEncryption(128)
with open('output.txt', 'w') as f:
for i in range(len(MSG)):
ct = AE.encrypt(i, MSG[i])
f.write(ct.hex()+'\n')if __name__ == '__main__':
with open('messages.txt') as f:
MSG = eval(f.read())
main()
Consequently, if we have a known plaintext-ciphertext pair and the same cipher stream is reused for another ciphertext, we can determine the cipher stream and decrypt the remaining ciphertexts.
What is AES in CTR Mode?
AES (Advanced Encryption Standard) cryptography in CTR (Counter) mode is a method of encrypting data that combines the security of AES with a stream cipher-like functionality, making it highly suitable for encrypting large data streams and enabling parallel processing. Here’s a breakdown of how AES in CTR mode works and what makes it unique:
领英推荐
1. AES Cryptography Overview
2. What is CTR Mode?
3. How AES in CTR Mode Works
Vulnerability Analysis
All cipher streams will be generated using the same key because [[...]*16] * len(MSG) in Python only creates four references to the same key, rather than unique keys. This can be easily tested.
$ python3 -q
>>> import os
>>>
>>> keys = [[b'\x00']*16] * 4
>>> keys
[[b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00'], [b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00'], [b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00'], [b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00', b'\x00']]
>>>
>>> for i in range(len(keys)):
... for j in range(len(keys[i])):
... keys[i][j] = os.urandom(1)
...
>>> keys
[[b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0'], [b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0'], [b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0'], [b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0']]
>>>
>>> keys[0]
[b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0']
>>> keys[1]
[b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0']
>>> keys[2]
[b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0']
>>> keys[3]
[b'\x06', b'e', b'6', b'\xd8', b'l', b'\xa4', b'\xaa', b'[', b'\xbd', b'\xad', b'\xa0', b'w', b'\xc3', b'\xac', b'\x8d', b'\xa0']
Therefore the flag can then be obtained:
>>> from pwn import unhex, xor
>>>
>>> P = [
... 'This is some public information that can be read out loud.',
... 'No one can crack our encryption algorithm.',
... 'HTB{?????????????????????????????????????????????}',
... 'Secret information is encrypted with Advanced Encryption Standards.',
... ]
>>> C = '''2ac199d1395745812e3e5d3c4dc995cd2f2a076426b70fd5209cdd5ddc0a0c372feb3909956a791702180f591a63af184c27a6ba2fd61c1741ea0818142d0b92
... 30c6d0cd775b16c23c3f103a1fd883c4632c11366fbc07d92088cc5ddc0a0c373aef3f12c7606c114f546c7f6e00c87a
... 36fdb2d97d0a5bcf0225586a1e8abfc62d3057273aab5ae5309d8c4ade060a236aed070d817b2c14110e590b1b27ef5d4d35ddc001b47d6c2bca00101c25039a
... 2dcc93d07c4a16c833375f2b00d894c62c2d442d3cf90cd43183c559c10006372cea2c1595487c0f4314091c0c268b120f3aaabe7bd31c0c05977a7f7c4f6ce6f59392e0e522e66500e153f7a6f914c7
... '''.splitlines()
>>>
>>> xor(unhex(C[2]), unhex(C[3]), P[3].encode())
b'HTB{d4mn_th3s3_ins3cur3_bl0ckch41n_p4r4m3t3rs!!!!}\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\xa7\x1d\x0ej\xfdK\xcf\xcfv\xe4b\xf3\xde\x1c\xd9l'
>>>
You can also watch: