Dans les sources, on trouve un "faux random":
rd.seed(s[n]); return rd.randint(0,30) == f and c(rest, n + 1)
On va donc se faire un script rands.py qui va constituer un array FS
tel qu'on ait FS[i] = {rd.seed(s[i]); rd.randint(0,30)} et on pourra se passer
dorénavant du random
Le reverse, c() facile
Au vu des sources (maintenant sans random), le principe semble assez similaire à
Renverse la tour 1/2 (Reverse)
:
un "chiffrement" en plusieurs étapes qu'on devrait reverse
Il est aisé de voir que le c() réalise en fait une comparaison de chaines de caractères
via match: on peut donc se passer de cette fonction.
On tester des mots de passe de longueur différente, jusqu'à ce que b()
soit de la même longueur que FS
Brute force?
Comme on aime la chance, on va essayer de voir ce qu'il se passe si on change le 1er
caractère de notre mot de passe. On compte le nombre de correspondances correctes entre
b()[i] et FS[i]: la meilleure sera considérée comme le bon char
Ma screen n'est pas correcte ici car le at_place n'était pas 0.
En revanche, on voit bien qu'une seule valeur (ASCII 78) a maximisé le nombre de correspondances
justes entre b() et FS, signifiant que ce caractère est un N (ASCII 78 ou 0x4E)
On se scripte donc un brute force caractère par caractère et on admire le mot de passe
s'écrire peu à peu devant nous
Comme c'est un peu long et que mon laptop est un peu vieux (et lent), j'ai stoppé le brute force
pour réduire l'intervalle de valeurs possibles à 32,128 (caractères ASCII imprimables)
Flag
Et pouf, le flag arrive! On confirme avec le challenge original qu'il est correct:
404CTF{M3RC1_PY7H0N3.10_P0UR_L3_M47CH}
On n'était même pas obligé de supprimer le "random": le même bruteforce aurait sans doute
marché en le conservant.