middle-grade.py
from random import SystemRandom
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes , bytes_to_long
from hashlib import sha256
from functools
import reduce random = SystemRandom()
def encrypt(plain, key):
Ks = [(key >> (12*i)) & (pow(2, 12) - 1) for i in range(4)]
reduce(lambda D,K:print(D,K) , Ks, plain)
return reduce(lambda D,K:AES.new(sha256(long_to_bytes(K)).digest(), AES.MODE_ECB).encrypt(D) , Ks, plain)
if __name__ == "__main__":
key = random.getrandbits(48)
flag = b"GH{__fakeflag__}"
assert (len(flag) == 16)
plain = b"iloveGH!"*2
print("plain = '{}'".format(plain.decode()))
print("cipher = '{}'".format(encrypt(plain, key).hex()))
print("encrypted_flag = '{}'".format(encrypt(flag, key).hex()))
plain = 'iloveGH!iloveGH!'
cipher = '0f973d8234b8086847b9d423617a19b0'
encrypted flag = 'bc4daf41f71e4dd958a852bbbbf8dfec'
Explication du programme
Le programme génère une clé de taille 48 bits. Ce nombre est ensuite séparé en base 4096 (2**12):
>>> from random import SystemRandom
>>> random = SystemRandom()
>>> key = random.getrandbits(48)
>>> key 146736011813222
>>> Ks = [(key >> (12*i)) & (pow(2, 12) - 1) for i in range(4)]
>>> Ks
[358, 3522, 1187, 2135]
>>> sum([Ks[i] * 4096**i for i in range(4)])
146736011813222
Chacun de ces nombres va être converti en bytes, puis hashé en sha256. L’ensemble va composer les 4 clefs AES, qui vont successivement chiffrer le clair, en mode ECB, dans la fonction encrypt.
Exploitation
De base, le nombre de possibilité est de 4096 * 4096 * 4096 * 4096 = 2**48.
Cependant, vu qu’on a accès à un clair et son chiffré, et vu le nombre faible de possibilité à chaque étape, il est possible d’effectuer une attaque dite « Meet in the middle« .
Pour cela, on va tester toutes les possibilités pour la moitié des étapes dans un sens (4096 * 4096 possibilités), puis dans l’autre (4096 * 4096).
Le nombre de possibilités devient alors 2 * 4096 * 4096 = 2**25, qui est nettement moins.
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes , bytes_to_long
from hashlib import sha256
import binascii
plain = b'iloveGH!iloveGH!'
cipher = binascii.unhexlify('0f973d8234b8086847b9d423617a19b0')
possibilities = ""
for i in range(4096):
s1 = AES.new(sha256(long_to_bytes(i)).digest(),AES.MODE_ECB).encrypt(plain)
for j in range(4096):
s2 = AES.new(sha256(long_to_bytes(j)).digest(),AES.MODE_ECB).encrypt(s1)
possibilities += "%s %d %d\n"%(s2.hex(),i,j)
with open('possibilities.txt','w') as f:
f.write(possibilities)
possibilities = ""
for i in range(4096):
s1 = AES.new(sha256(long_to_bytes(i)).digest(),AES.MODE_ECB).decrypt(cipher)
for j in range(4096):
s2 = AES.new(sha256(long_to_bytes(j)).digest(),AES.MODE_ECB).decrypt(s1)
possibilities += "%s %d %d\n"%(s2.hex(),i,j)
with open('possibilities_reverse.txt','w') as f:
f.write(possibilities)
Le script va nous générer l’ensembles des différentes possibilités (en chiffrant dans « possibilities.txt », et en déchiffrant dans « possibilities.txt »). Plus qu’à trouver l’élément commun:
# On trie les deux listes cat possibilities.txt |awk '{ print $1 }'|sort > p1.txt
cat possibilities_reverse.txt.txt |awk '{ print $1 }'|sort > p2.txt
# On récupère l'élément commun des deux listes comm -12 p1.txt p2.txt
# 81e11c88273f0f7ea2fded6c389189c0
# On récupère les clefs de chiffrement
cat possibilities.txt possibilities_reverse.txt |grep 81e11c88273f0f7ea2fded6c389189c0
# 81e11c88273f0f7ea2fded6c389189c0 638 2697
# 81e11c88273f0f7ea2fded6c389189c0 2557 2561
On peut alors déchiffrer le flag avec Ks (638, 2697, 2561 et 2557).
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes , bytes_to_long
from hashlib import sha256 import binascii
plain = b'iloveGH!iloveGH!'
cipher = '0f973d8234b8086847b9d423617a19b0' 7
encrypted_flag = 'bc4daf41f71e4dd958a852bbbbf8dfec'
Ks = [638,2697,2561,2557]
tmp = binascii.unhexlify(encrypted_flag)
for i in Ks[::-1]:
tmp = AES.new(sha256(long_to_bytes(i)).digest(),AES.MODE_ECB).decrypt(tmp)
print(tmp)
Flag
GH{m33t_t1me_:)}