---
# System prepended metadata

title: Write up DG Hack Involucrypt 2

---

# Write up DG Hack Involucrypt 2
**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 involucrypt2 (clé)(exemple):
```
‹+ÉguƒeÛïþ,«c.¾[\”1…Ä3–Š’ ¥‚à«	‹ÓS^Ö¾•@QõÔKÇÓ¶Ë|O_'ç/¥{=0 ìÂôò—‡ WËzL‹ëïkaãBmToìÚÚgÓ¬äÉkŽGæ2þ%¶M:¡!³Nl‰$h"˜»#²l¨…›¾ØÚBRVüzõË–þä‚øØÒî4+y«¼],3|×¦[/7¤N|2
¾Á Ùg(¤Á3»„¨BþcC¬ƒØwÝÝ¶² &{GO¼jÆa¯°ýíÄåøJèºç;L¶Œ>XI¤‚MTYñØè"
uŸëîèOú	 §Ü·ï"ó>õKƒ%eÎ‰´D‹Ú÷oSw-Ýà†mW ¤H“+9œsØ;¼o 0ËšóuÈûNˆ=KÏSÍV÷T]XÛé OM‰?(¸Ë^+¡
4wÓ¤gÌÃ*‡’žÝe·R:ºžúÛsòžlí‡[lƒ¬›*åMÙFRQT-TÆô¾yÂ¼ÛBð,7éGÍÀ{‚*ÛGJbªéÅI"ôÎòºbG‹ªõ,²9«‚×öñ1©#ÇbíYÃ¼åßØ»SÃ¥ìýÓ‡R¯_·qÕÕh'zœVÏ@JŒ¯Qü˜§˜9uìB¶¼›c!ÊÁ
çúéãÞKé ¯CÛx´dð“§iRûÓP*ã©˜Jj4`@W¼ž!ö,H†›ín¤Ž¦0¸Ž¹(Õô¹Á
HLªf!^ÅùMÞG¨'5‹5ð,«TîÛú-ÕžƒßÎmgàbƒ³ûa©™3 6Q¨Dtês£>FHy;éÔÄêÞ•^P²yqH!iÈµ`:‘ ô=0©/¯ƒ÷¾'ÏpE`×±7VŽ;í$óÏ*¶à‚¶²"Òç' ¿+He3Çu·E"½b©k@Ë$Š«–m7­À`(„­ÇC¥„=ÖÑ ^O¯Ä.ÙÂF.K*–<üQÑb,)°Ñ€U›&Áf´aÁ 8ƒÈ¾Æ¼~öf8YÞÉÕ•^
FðÞ"Úž¾6rÁÐtLÀ{ËöW	7Ô±¯m)m;€FÇN„³1/jÅg„Á]C™cŠ#i“þ‚ÛÐü7çîý(¾ê¥Îm|©­ÖcÀÈþ5rÏÀû~«V< aJ—Ãî)×PYwàÕ¾¶Ã2rŽQ)IW(a«3ž‘ÐéSªj‚¹©ê:p·¶ÊI¸>Ì¯g{ìé™nj/qÕÑyøJ
IÛµNWONo¥á+<ùØÍ7¨Ó©ë˜èå`–µà<òI"$²³X¿€¤	s‚
ëÃ,Î" Hñ«Ï»KtºÙã¬gDÙ$“bš`X<Í³‡Wövoìƒ~žá)ëH›`1>;|+W‚7ã°Iø¼v›<–b!¨©¡'&CaP·O¼×ÒêÕÃÄ>uâñLË—¥âöïT®—Þª,ôÙì"Ð[Á9÷è¤ê—Š|ƒŽ‹/„iÄ	“2«L3ßÇR!^;[S
¨­îE›Ï˜}Ð›¶*ŽbI`½&’Å\aU‰X Çaí¤ÍOÑ«Á¼û;ìoÐ}uLâœâx?úÙ*íŠmK( ká\,7Œ".g¼€HÔ’gV¥©=IH#ùr,ýc£\Ù÷ÚY.µ6Â²umM…IÓdl6ÎØöÛø23*MÏß¬yDZô06D`-“â(Ö@÷Ú^cÔ/&ë@aóAOì.Ë|ÑÐÖIm  ŽDw(úì
ÒÂÌÊ9bu‹¯ð>L–Çæs
êÎŒÄ‰ÑÑÕ¬m²æï•&ô­—,¡æ…j«¸s&2à = ï‚™ŽG§–¡*r]ÜAï˜dŒ Ï´ˆ™`ê«
ý²¥Å>(…ju7>+…(q  Â¡ÀjpÝè·ÚÞ0}ò	ääCª½Kn²@¢]g4KíºçFX:Ó¥œg¡
```


---

# Analyse
Alors, me revoila assez vite avec le meme algo de chiffrement, mais un texte chiffré de 1497 caractères cette fois.
*Si vous voulez bien comprendre ce write up, allez lire celui que j'ai fait pour [Involucrypt1](https://https://hackmd.io/@ctp/HkYgKLdcv).*
On a toujours la meme taille de bloc:

```
BLOCK = 150
for _ in range(BLOCK):
```

On remarque que notre clé qui fait 1497 caractères, a été chiffrée à partir des 10 premiers caractères de la clé. (150+150+150+150+150+150+150+150+150+147).

Le temps de chiffrement de 1497 caractères est plus long que pour les 370 d'Involucrypt1. Et puis il y a 10 caractères à brute force.

Il suffit de trouver les 10 premiers caractères de la clé, allé hop dirction OVH pour louer un VPS à 1400€ et le tour est joué. 

Je plaisainte. J'ai un pc portable de 2012, il va falloir faire avec.


---
# Résolution: Brute 'finesse'

Cette fois, stratégie différente. Vu que le challenge est à 200 points, j'imagine que la clé n'est plus seulement composée de lettres et de chiffres, mais aussi de caractères spéciaux ({,#,~,/,& etc)

Je pars encore une fois du code python fourni, mais cette fois ci, j'utilise comme alphabet tous les caractères compris entre 32 et 126 de la table ASCII.
![](https://i.imgur.com/ZzuUz0A.gif)

Comme pour Involucrypt1, je vais détecter le texte en clair en comptant le nombre de caractères visibles (compris eux aussi entre 32 et 126). Mais attention, vu que le texte est long il doit y avoir des sauts de ligne. Donc je décide de chercher jusqu'à que j'obtienne un texte avec **1430**/1497 caractères visibles. (quitte à devoir chercher manuellement les derniers).

Comme je l'avais remarqué dans Involucrypt1, lorsqu'on trouve l'un des caractère de la clé, il y a un plus grand nombre de caractères visibles dans le texte déchiffré.

Je décide donc d'essayer tous les caractères à la première position de la clé, puis de conserver le meilleur (celui qui donne le plus grand nombre de caractères visibles dans le texte déchiffré). Puis pareil pour le 2eme caractère de la clé, etc, jusqu'au 10eme. Une fois arrivé au 10eme, je recommence en essayant tous les caractères à la place du premier, puis du 2eme, etc en faisaint chaque fois augmenter le "score" de caractère visibles.



Voici ca que ca donne au niveau du code:

```
import itertools
import operator
import sys

BLOCK = 150
with open('involucrypt2(1)', 'rb') as f:
    read_data = f.read()
    t=len(read_data)


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 []
    o=49
    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))
            #key.append(o)
            random.shuffle(key)
        if len(key) == length:
            break
        o=o+1
    return key


def encrypt(string, key):
    key = keystream(map(ord, key), len(string))
    stream = itertools.starmap(operator.xor, zip(key, string))

    return bytearray(stream)
    #return bytearray(key)


def keystream2(seeds, length, base=None):
    key = base if base else []
    o=49
    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))
            key.append(o)
            random.shuffle(key)
        if len(key) == length:
            break
        o=o+1
    return key


def encrypt2(string, key):
    key = keystream2(map(ord, key), len(string))
    stream = itertools.starmap(operator.xor, zip(key, string))

    #return bytearray(stream)
    return bytearray(key)


if __name__ == "__main__":
    
    #ouverture + fermeture en w pour remettre le fichier à 0
    f = open("demofile3.txt", "wb")
    f.close()
    
  
    if sys.version_info <= (3, 0):
        sys.stdout.buffer.write(encrypt(sys.stdin.read(), sys.argv[1]))
    else:

        cle="HJeB0cbqazaeadffssse<<se<sf<eszrzeffqtehethetheethehetha"
        txt="bite"

        bestCle="azerty"

        k=0
        l=0

        bestVis=0

        while bestVis < 1430:
            #construire la clé à essayer
            qq=l%10
            #couper la chaine en 2 au niveau du caractère à  remplacer
            tmp1=cle[0:qq]
            tmp2=cle[qq+1:]

            #initialisation du meilleur caractère
            bestChar=cle[qq]
            
            #on essaye les 126-32=94 caractères
            for a in range(32,126):
                
                #contruction de la clé
                cleTmp=tmp1+chr(a)+tmp2
                print(cleTmp+" best="+str(bestVis)+"/"+str(t)+"\r\n=>best clé="+bestCle+"\r\nl="+str(l)+"\r\n")
                fg=encrypt(read_data, cleTmp)

                
                #compte le nombre de caractères visibles
                count = 0
                for i in range(0, len(fg)):

                    if (fg[i]>=32) and (fg[i]<=126):
                            count=count+1
                    
                #si on trouve une meillure clé, on remplace la notre
                if count > bestVis:
                    bestVis=count
                    print("plus grand vis: "+str(bestVis)+"/"+str(t)+" pour cle="+cleTmp)
                    bestChar=chr(a)
                    bestCle=tmp1+bestChar+tmp2
                    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()
            #reconstitution de la clé
            cle=tmp1+bestChar+tmp2
            l += 1
            k=k+1
                
    f.close()

```

Plus qu'à attendre. Au bout de 8h, le matin on commence à avoir des truc intéressants: 

```

----------->CODE:0
It's someÅong abouthya giyl,
~l~~~~~~~~~~~~~~~~~~~~~~~º~~~
that j8st ™akes$my head wanna twirl, 8~~~~~~~~~~~G~~~~~~~~~~~~~~~
oh you got me want to tell allêthpm ÛtŒer girls,
~~~¼~~~~~~~~~~~~~~~~~~~~~~~~~
t€eóe's nothing 1lsi bettera@n this wUrld!
~~~~~Õ~Ô~~~~~~á~~~~¶~~~~~`~~~
the momenÚ i seen her¾i w‹s 1n shock
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
so shocked you would've Ûink I've ¢u,t been shot,
~~~~~~~ú~~@~~~~°~á~~~~™~~~~~~
sÎt down reg
ƒ now i ·his spot.
~W~~~~~~~~~~~~~~~~~~~~~~~~~¯~
But ~oo bad thYs dîesn't happe& a lo».
~~~~~Z~~~Ù~J~}~~~~~~~~~~~~~~>
Ow grl you gÎt Üe ôisualizing me on top,
~~~~~~~~¦~L~~~~ÿ~~~~÷~~~~~~~~
on&
op of your hot body whilÈ were sweating a lot,
~~~~~~~~~~~~~~~~~~~~~~~~~y~~~
a lot of time on th cock before we have	to stop.
~~~~~~~ø~4~~~~~~~$~~~~~~~~~ž~
too bad /hez not i&to that stuff a lot,
~~~~~~~~~~~~~~~~~~~~~~~~~=Ý~
oh ½xnOshes really super hot,
~~~~~7~~~~~+~~~~~~~~~~~€~~~~~
hotter than t¥e&sun thŸt's right on top,
~~c~~~~~~~~~~~~~Së~~~~~~~~~~~
Oh man there she goes iEhad to stop,
~~~1~~~~~~¿~~~~~~~~~K~~q~~~~
“nd ank her some quesions thatii had án ±tock.
~~~~~~~~~~~Ö~~¾~~~~~~~~~~~~~~
SÄe said phe wanths to take 3t slowÌ
~~~~K~~~~~À~~~~~~~~~~~~~'~~~wð[m nt tat type on guy ill let c0a knowa
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
when i °ee ,hat red l¬gh* all]i know is go,
~~”~~C~~~~~~È~q~~~×Î~~~~~~~~
so baby girl lets do this on the floor.
(~~~~~~~~~~~~~~~~~~~~~~~~~~~~
show me your moves¾ha makes you such a pro


*the flag±is sup:hotfire*

----------->CODE:0
It's some8*Íng aboutÜya gi:l,
~Ò~~~~~~~~~~~~~~~~~~~~~~~Q~~~
that jwst 	akesZmy head wanna twirl,ÝÙ~~~~~~~~~~~|~~~~~~~~9~~~~~~~
oh you got me want to tell allIthám it²er girls,
~~~m~~~~~~~~~~~~~~~~~~~~~~~~~
tfee's nothing lsM betteråÂn this w rld!
~~~~~8~q~~~~~~´~~~~K~~~~~N~~~
the momenŽ i seen herði ws .n shock
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
so shocked you would've A­ink I've ®uõt been shot,
~~~~~~~%~~ú~~~~É~ã~~~~A~~~~~~
sUöt down r1g‚q now i7 °his spot.
~w~~~~~~~~~~~~~~~~~~~~~~~~~ ~
But (oo bad th/s dîesn't happeŠ a loÆ.
~~~~~~~~y~i~½~~~~~~~~~~~~~~4
OC grl you g_t >e isualizing me on top,
~~~~~~~~±~§~~~~ñ~~~~?~~~~~~~~
on2	op of your hot body whilT were sweating a lot,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a lot of time on thX cock before we haveBto stop.
~~~~~~~Š~e~~~~~~~°~~~~~~~~~¹~
too bad Lheû not iSto that stuff a lot,
~b~~~~~~~~~~~~~~~~~~~~~~~~é§~
oh kan¦shes really super hot,
~~~~~~~~~~Ñ~~~~~~~~~~~¦~~~~~
hotter than t¯eƒsun thít's right on top,
~~ï~~~~~~~~~~~~~]C~~~~~~~~~~~
Oh man there she goes i§had to stop,
~~~†~~~~~~ì¦~~~~~~~~~'~~D~~~~
Ÿnd auk her some quesions that@i had ën °tock.
~~~~~~~~~~~¥~~~~~~~~~~~~~~~~
Sèe said  he wantrs to take Út slowÈ
~~~~p~~~~~:~å~~~~~~~~~~~~0~~~¶?m nt tíat type o½ guy ill let cña knowŽ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
when i çee shat red lBghv allÂi know is go,
~~Æ~~.~~~~~~õ~O~~~e7~~~~~~~~
so baby girl lets do this on the floor.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
show me your moves£,haõ makes you such a pro


*the flagXis sup[hotfire*
```
Je peux presque déchiffrer à la main. Mais bon on verra à midi cette fois aussi. A midi, Jackpot:
```
----------->CODE:0
It's something about ya girl,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
that just makes my head wanna twirl,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
oh you got me want to tell all them other girls,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
there's nothing else better in this world!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
the moment i seen her i was in shock
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
so shocked you would've think I've just been shot,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
shot down right now in this spot.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
But too bad this doesn't happen a lot.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Oh girl you got me visualizing me on top,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
on top of your hot body while were sweating a lot,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
a lot of time on the clock before we have to stop.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
too bad shes not into that stuff a lot,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
oh man shes really super hot,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hotter than the sun that's right on top,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Oh man there she goes i had to stop,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
and ask her some questions that i had in stock.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
She said she want's to take it slow,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
i'm not that type of guy ill let cha know,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
when i see that red light all i know is go,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
so baby girl lets do this on the floor.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
show me your moves that makes you such a pro


*the flag is supahotfire*

```

I'm not a wrapper but I got the key !

Dans la console ca donne (extrait):
```
_jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha best=1372/1497
=>best clé=2jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
l=90

plus grand vis: 1373/1497 pour cle=_jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
`jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha best=1373/1497
=>best clé=_jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
l=90

ajjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha best=1373/1497
=>best clé=_jjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
l=90

plus grand vis: 1453/1497 pour cle=ajjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
bjjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha best=1453/1497
=>best clé=ajjfisptoiaeadffssse<<se<sf<eszrzeffqtehethetheethehetha
l=90
```

Les 10 premiers caractères sont donc "ajjfisptoi". Le script a tourné pendant environ 10h sur mon pc, et l=90 signifie qu'on a essayé 90 fois toutes les possibilités d'un des 10 caractères de la clé. Donc que chaque caractère a été retesté 9 fois avant de trouver.



---
# Analyse

1. La clé contient juste des minuscules et il y a le mot "flag" dans le résultat, au final j'aurais pu faire plus simple en réduisant l'alphabet. 
2. Par contre en cherchant le mot "flag", le code se serait arreté trop tôt car le mot apparait avant la clé.
3. Si le flag avait été noyé au milieu de caractères "non visibles",(crypto+little stegano) mon code n'aurait pas fonctionné, il n'aurait pas détecté le texte en clair.
4. Mon code réduit à 90*94=8460 le nombre de combinaisons contre 94^10=53861511409489970176 en brute force pur.


a+
Shurex / CTP