---
# System prepended metadata

title: Write up DG Hack Involucrypt 1

---

# 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 Jredoso Š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þãit¦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