# Write up DG Hack Involucrypt 1 **Consignes:** Pendant une investigation numérique sur un disque dur, vous trouvez un fichier chiffré en utilisant un algorithme propriétaire. Heureusement, le script utilisé pour chiffrer et déchiffrer se trouve dans le même dossier, mais vous ne disposez pas de la clé. Bon courage ! Fichier crypt.py (algo) ``` import itertools import operator import sys BLOCK = 150 class mersenne_rng(object): def __init__(self, seed=5489): self.state = [0] * 624 self.f = 1812433253 self.m = 397 self.u = 11 self.s = 7 self.b = 0x9D2C5680 self.t = 15 self.c = 0xEFC60000 self.l = 18 self.index = 624 self.lower_mask = (1 << 31) - 1 self.upper_mask = 1 << 31 # update state self.state[0] = seed for i in range(1, 624): self.state[i] = self.int_32( self.f * (self.state[i - 1] ^ (self.state[i - 1] >> 30)) + i ) def twist(self): for i in range(624): temp = self.int_32( (self.state[i] & self.upper_mask) + (self.state[(i + 1) % 624] & self.lower_mask) ) temp_shift = temp >> 1 if temp % 2 != 0: temp_shift = temp_shift ^ 0x9908B0DF self.state[i] = self.state[(i + self.m) % 624] ^ temp_shift self.index = 0 def get_random_number(self): if self.index >= 624: self.twist() y = self.state[self.index] y = y ^ (y >> self.u) y = y ^ ((y << self.s) & self.b) y = y ^ ((y << self.t) & self.c) y = y ^ (y >> self.l) self.index += 1 return self.int_32(y) def get_rand_int(self, min, max): return (self.get_random_number() % (max - min)) + min def int_32(self, number): return int(0xFFFFFFFF & number) def shuffle(self, my_list): for i in range(len(my_list) - 1, 0, -1): j = self.get_rand_int(0, i + 1) my_list[i], my_list[j] = my_list[j], my_list[i] def keystream(seeds, length, base=None): key = base if base else [] for seed in seeds: random = mersenne_rng(seed) for _ in range(BLOCK): if len(key) == length: break key.append(random.get_rand_int(0, 255)) random.shuffle(key) if len(key) == length: break return key def encrypt(string, key): key = keystream(map(ord, key), len(string)) stream = itertools.starmap(operator.xor, zip(key, map(ord, string))) return bytearray(stream) if __name__ == "__main__": if sys.version_info >= (3, 0): sys.stdout.buffer.write(encrypt(sys.stdin.read(), sys.argv[1])) else: sys.stdout.write(encrypt(sys.stdin.read(), sys.argv[1])) ``` Fichier involucrypt (clé)(exemple): ``` hÙÏâ¼óՏ‹Ä G:¿¿ƒƒÿCÝ&K²uj†´WÝqö.áÁÐÛ_˜ãKPC¤mY?<˜-å^Mñ_%߈/]Hy&”x^èØÂ$(Öݾ/as›„÷?´PƒKI½ ‰ìФˆ4ÍkÞw¶ŠÄ’ouv x Ÿî ìÇž¢x ÆEB÷™X1Í6¸çè]H¬Ëýÿ©-Û={I*e¢§>¢µC°©0,ûòŠÈöf]>.%É~ÂûY/ˆMݨl{¶ÔØAϯð\&J¶÷2&«ÈÀû8vÊòê6E¡«UÌïq‹Ñ» Ãm­ý’üjÚíßhó™AᨦLå/”îô«Àñ¶³½î+ºAx²Ü+‘³:ƒ|°dUE,Ð&u«,ðRo5‡›Ûñ"”¤:¤^§ÈU0üwǐ¢¼`Œgê€ãx%r)ùþo¬”<˜Üa1=÷b¸ê#•f…3qÐöËT½‡ª%7Êöç~‘ ``` --- # Analyse En gros l'algorithme conciste à générer un nombre aléatoire avec le premier caractère de la clé, chiffrer 150 caractères du texte clair avec, puis reprendre un nombre aléatoire à partir du 2eme caractère le clé, chiffrer 150 caractères du texte clair avec, etc. Les caractères chiffrés sont mélangés les uns aux autres. ``` BLOCK = 150 for _ in range(BLOCK): ``` On remarque que notre clé qui fait 370 caractères, a été chiffrée seulement à partir des 3 premiers caractères de la clé. (150+150+70). Il suffit de trouver les 3 premiers caractères de la clé et le tour est joué. Si on s'amuse à remplacer chaque caractère d'un texte chiffré quelconque par l'indice du caractère de la clé qui l'a chiffré ca donne par exemple: ``` 2311212333231222123121111222323133231123112111111222323112211322211221121311312221112112222123212232322233112132222112212122113113223121131221122132112111112221211111122222222323223112231111131112222232211221232132131213212311223211321322112313213111232112121231322112322112223231213213111221232121223213111232213212231112132112122133132221112322121221113313221211313113 ``` On voit que les caractères sont bien mélangés --- # Résolution: Brute force Je pars carrément du code fourni et j'essaye toutes les combinaisons des 3 premiers caractères de la clé, j'analyse le texte déchiffré, puis je passe au suivant. Comme alphabet pour les 3 premiers caractères de la clé, j'utilise toutes les lettre majuscules, les lettres minuscules et les chiffres. Soit (26+26+10)^3=238328 possibilités. Ca fait long de toutes les écrires dans un fichier texte, puis de les vérifier à l'oeil nu. Il faut une astuce. Plutôt de chercher les mots Flag, flag ou FLAG dans le texte déchiffré (je me dit que ca sera peut être pas écrit), je préfère compter le nombre de caractères "visibles" dans le résultat. Par visible, j'entends compris les caractères compris entre 32 et 126 dans la table ASCII. ![](https://i.imgur.com/ZzuUz0A.gif) Voici ca que ca donne au niveau du code: Je rajoute l'alphabet et l'ouverture du fichier contenant le texte chiffré ``` with open('involucrypt2(1)', 'rb') as f: read_data = f.read() t=len(read_data) cars = ['A', 'B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1','2', '3','4','5','6','7', '8','9'] ``` ``` bestVis=0 pad="sdfpisqhmofi" for a in cars: for b in cars: print("avancement="+str((k/62)/38.44)+"/100, cle debut="+cle[0:10]) #pour le suivi de progression for c in cars: #création de la clé cle=chr(a)+chr(b)+chr(c)+pad #on decrypte avec cette clé fg=encrypt(read_data, cle) # On compte le nombre de caractère visibles dans le résultat count = 0 for i in range(0, len(fg)): if (fg[i]>=32) and (fg[i]<=126): count=count+1 #on enregistre le meilleur résultat. Dès que le meilleur résultat est battu, on l'écrit (ca permet de voir la progression) if count > bestVis: bestVis=count print("plus grand vis: "+str(bestVis)+"/"+str(t)+" pour cle="+cle) f = open("demofile3.txt", "ab") f.write(bytes("\r\n----------->CODE:",'UTF-8')) f.write(bytes(str(k), 'UTF-8')) f.write(bytes("\r\n", 'UTF-8')) f.write(fg) f.close() ``` Plus qu'à attendre. Au bout d'une nuit, le matin en partant qu travail je vois dans le fichier résultat: ``` ----------->CODE:21668 The”f.œ9Ô(s:?PhôKey=sTooDamn½6ak£ˆ Lo etGk3süm âolor Kit a”üÛ,rc[nsct¸tu‚ adip„rXAJ!ûliŸ, s˜d Æo Râucm×d tem„Ìræi{´KxiduntãG la,o`e Tt dªlo3s mÊgna„åliqua. ‰ —nimÔaè miº,m€4!ni*m, ÙusYÆt×ud¾exer‡ì„tiì; ¸Ya¥éo 9=ori‰ —i›i o(Ôaliuip ex Öa comåodw roŽequat Duiƒ aute i Jredoso Škrøayehžcdelit in vÇluptn-e6vezt e:sb Ñöllum ÄloS<  _¿8iat n¸lla pÄ¡ýat"r. ----------->CODE:25512 TheGf„~qÅs:gh•KeyãsTooDamnèÔakžˆ LoYe0²Ï?sÆm #olor ¢it a\,$,cc(ns=ctÑtu¤ adip˜ð'¿'ÕLliÀ, sd \o y.uam»d tem±*r_i LÝDiduntÛ€l laçoáe pt d«loâ7 m³gna"\liqua. ?; )nim0aŽ mi¹ØmÊniîm, yuþsþãi t¦udAexerZ'˜>ti/Š S(úaDo ²Î¿orié ïi i ›¤-aliŒuip ex }a com³odM Ýou$equatA Dui aute i_êreœdo£oŒ šiFrL7Îeh–Nderit in vÖluptZe veCt eÛsO [ãllum ýHlo3 Ô Äiat n}lla p/vatxr. ``` Ca sent bon on va attendre encore un peu. Puis à midi: ``` ----------->CODE:102392 The flag is: TheKeyIsTooDamnWeak! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ``` Houra ! On a la clé ! Dans la console ca donne (extrait): ``` avancement=42.89802289281998/100, cle debut=ak9 avancement=42.92403746097815/100, cle debut=al9 avancement=42.950052029136316/100, cle debut=am9 plus grand vis: 364/370 pour cle=anesdfpisqhmofi ====>trouveanesdfpisqhmofi ``` Les 3 premiers caractères sont "ane". Un message caché de la DGA ? --- # Analyse Par curiosité j'essaye de retrouver les clés qui ont généré les messages qui "sentaient bons" à 8h du mat. Cela revient à convertir les nombres 25512, 21668 en base 62. Ces nombres c'est la nieme clé essayée. (1ere AAA, 2eme AAB, etc) Avec cet alphabet:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 [6, 39, 30](62) (soit clé='Gne') pour 25512 [5, 39, 30](62) (soit clé='Fne') pour 21668 Je remarque que dès que certains caractères de la clé sont corrects, certains, du message déchiffré, le sont aussi. Un hint pour Involucrypt 2 ? Oui. a+ Shurex / CTP