5 perc
Cryptospiracy theory
1. rész
Forrás
A feladathoz egy zip fájl és egy weboldal címe van megadva.
A weboldalon felhasználó nevet és egy jelszót kér.
A zip-ben egy bináris encrypted_message.aes
és egy generator.py
található.
A python fájl tartalma:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import binascii, string
with open('password.txt') as pwd_file:
pwd = pwd_file.read().strip()
# check printability
assert all(k in string.ascii_letters + string.digits for k in pwd)
pwd = pwd.encode()
if len(pwd) not in [16, 24, 32]:
raise ValueError("Invalid AES key length. Key must be 16, 24, or 32 bytes long.")
with open('message.txt', 'r') as message_file:
message = message_file.read().strip()
words = message.split()
ciphertext = b""
cipher = AES.new(pwd, AES.MODE_ECB)
for word in words:
word_padded = pad(word.encode(), AES.block_size)
encrypted_word = cipher.encrypt(word_padded)
ciphertext += encrypted_word
with open('encrypted_message.aes', 'wb') as file:
file.write(ciphertext)
print("Encryption complete. Ciphertext saved to 'encrypted_message.aes'.")
Magyarázat
A encrypted_message.aes
fájl tartalmát a generator.py
fájl hozta létre. A program egy password.txt
fájlból beolvas egy jelszót, amiről kiderül, hogy csak (angol) alfanumerikus karaktereket tartalmaz és 16, 24 vagy 32 karakter hosszú. Beolvas ezen kívül egy message.txt
fájlt is, amit szavanként feldarabol, és a jelszóval eltitkosítja AES-ECB titkosítással, majd a titkos blokkokat kiírja egy fájlba.
Tudni kell az AES-ről, hogy 16 bájt hosszúságú blokkokat fogad, és ugyan ilyen hosszú blokkokat ad vissza. Amikor a titkosítandó adat mérete nem pontosan osztható 16 bájtal, ki kell egészíteni. Ezt teszi a pad
függvény.
Az érdekesség, hogy a program az AES-t ECB módban használja, aminek egy nagy veszélye, hogy nem tesz entrópiát a titkosítandó adatba, azaz módosítás nélkül titkosít el minden blokkot. Ezzel az a baj, hogy az azonos blokkok mindig azonos eredményt adnak.
Cél
A zipben lévő üzenetet kell kititkosítani, abból pedig kiderül, mivel kell belépni az oldalra.
Megoldás
Ha kiszámoljuk a lehetséges jelszavak számát:
>>> 16**62 + 24**62 + 32**62
2085924877185928105442759926841284955304321885717825681976502561860327976404420042599490912256
kiderül, hogy nem lenne értelme elkezdeni bruteforceolni. Jobb ötlet, ha megnézünk inkább egy jelszólistát, hátha benne van. Ilyen például a rockyou.txt
, amiből kigyűjtjük a megfelelő jelszavakat: grep -E '^[a-zA-Z0-9]{16}$|^[a-zA-Z0-9]{24}$|^[a-zA-Z0-9]{32}$' rockyou.txt > passwords.txt
.
Ha feldaraboljuk a fájl tartalmát 16 bájtonként, feltűnik, hogy a 8332988eb6fafe7212c52c0ec20495b6
blokk 7-szer, míg a c151fc2e0b9cec7d36616c458029a99e
blokk 6-szor fordul elő. Azt tudjuk, hogy minden blokk egy szót jelöl (ha kisseb mint 16 karakter). Az angolban a két leggyakoribb szó a the
és a be
. Próbáljuk meg ezeket dekriptálni a kigyűjtött jelszavakkal, például a következő programmal:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
with open('passwords.txt') as pw_file:
pws = pw_file.read().splitlines()
encrypted_block1 = bytes.fromhex("c151fc2e0b9cec7d36616c458029a99e")
encrypted_block2 = bytes.fromhex("8332988eb6fafe7212c52c0ec20495b6")
for pw in pws:
pw = pw.encode()
cipher = AES.new(pw, AES.MODE_ECB)
decrypted_block1 = cipher.decrypt(encrypted_block1)
try:
unpadded_text1 = unpad(decrypted_block1, AES.block_size)
except ValueError:
continue
else:
print(pw, unpadded_text1)
text1 = unpadded_text1.decode()
decrypted_block2 = cipher.decrypt(encrypted_block2)
try:
unpadded_text2 = unpad(decrypted_block2, AES.block_size)
except ValueError:
continue
else:
print(pw, unpadded_text2)
text2 = unpadded_text2.decode()
#ha a két halmaznak van közös eleme
if {"the", "be"} & {text1.lower(), text2.lower()}:
print(pw)
Kiderül, hogy rögtön a 6. jelszó megfelelő: avengedsevenfold
.
Már csak vissza kell titkosítani az egészet. Egy egyszerű megoldás, ha feltöltjük a fájlt Cyberchefre, mivel itt is van AES titkosító.
A Cyberchef ugyan nem unpaddol minden egyes blokkot, csupán az egész üzenetet egyben, de a lényeg így is látszik belőle: a flag első fele HTB{Br3@k_Th3
, valamint a belépési adatok Username: Hacktheboxadmin Password: G7!xR9$sT@2mL^bW4&uV
2. rész
Forrás
Az oldalra belépve egy chat beszélgetést látunk, amiben két fájl is fel lett töltve: encrypted.txt és encryptor.py
A Python fájl tartalma:
from math import gcd
import random
def encrypt(a, b):
ct = []
for ch in msg:
if ch.isalpha():
encrypted_char = chr(((a * (ord(ch) - 65 - b)) % 26) + 65)
ct.append(encrypted_char)
else :
ct.append(ch)
return ''.join(ct)
msg = open('secret_message.txt').read()
while True:
a = random.randint(1, 26)
b = random.randint(1, 26)
if gcd(a, 26) == 1:
break
with open('encrypted.txt', 'w') as f:
f.write(encrypt(a,b))
print("Encrypted message saved to encrypted.txt")
Magyarázat
A program egy úgynevezett affin titkosítást használ. Ahol a titkosítás menete: \(E_{a,b}(x) = a \cdot x + b ~ mod ~ m\), a kititkosításé: \(D_{a,b}(y) = a^{-1} (a - b) ~ mod ~ m\), ahol \(a, b\) a kulcs, \(m\) pedig az ábécé mérete, itt \(m = 26\). Ehez véletlenszerűen generált kulcsot használ. Így titkosítja a secret_message.txt
tartalmát az encrypted.txt
fájlba (amit szintén megkaptunk). Ennek a titkosításnak nagy hátránya, hogy egy betűt eltitkosítva mindig ugyan azt az eredményt adja. Ezért frekvencia analízissel (a számok előfordulási arányát megállapítva) lehet egy jó tippünk a gyakori betűk titkosított megfelelőjére, amiből ki lehet számítani a kulcs értékeit.
Cél
Meghatározni a titkosításhoz használt kulcsot és kititkosítani a kapott fájlt.
Megoldás
A titkos üzenetben előforduló leggyakoribb betű a D
(3.), valószínűleg ez rejti az E
(4.) betűt. Ezt beírva a képletbe: \(3 = a \cdot 4 + b ~ mod ~ 26\). Tudjuk, hogy \(a\) és \(b\) is \([1,26]\) tartományban vannak, valamint \(gcd(a, 26) = 1\) (relatív prímek). Ezek alapján a 676 kulcsot leszűkíthetjük a következő 12 kulcsra: (1 25), (3 17), (5 9), (7 1), (9 19), (11 11), (15 21), (17 13), (19 5), (21 23), (23 15), (25 7). Lehetne a második leggyakoribb karakterre is kiszámítani, amivel tovább szűkülnének a lehetséges kulcsok számai, de ez is megfelelően kevés. A kulcsokat próbálgatva a (7, 1) kulcsra egy értelmes szöveg áll elő, benne a keresett flag második részével: _BL0CK_P@TT3RN}
Íme a program, ami visszatitkosítja az üzenetet (vagy használható a Cyberchef is):
from math import gcd
def decrypt(a, b):
a_inv = pow(a, -1, 26)
ct = []
for ch in msg:
if ch.isalpha():
encrypted_char = chr(((a_inv * (ord(ch) - 65 - b)) % 26) + 65)
ct.append(encrypted_char)
else :
ct.append(ch)
return ''.join(ct)
msg = open('encrypted.txt').read()
key= (7, 1)
with open('secret_message.txt', 'w') as f:
f.write(decrypt(*key))
print("Decrypted message saved to secret_message.txt")