34C3 Junior - TOP
Ce post suit mon précédent article n’hésitez pas à y jetter un oeil pour situer le contexte de ce CTF ! Non sans plus attendre, la write up pour un challenge crypto (eh oui) : top
0x1 Analyse
L’intitulé du challenge était : Perfectly secure. That’s for sure! Or can break it and reveal my secret ? J’ai l’intime impression que chaque application qui est dite perfectly secure ne l’est pas vraiment. Mais ça ne doit être seulement qu’une impression … Le code source était associé à un fichier avec un contenu binaire, go le déchiffrer alors :
En analysant le code source on remarque (j’ai pas dit immédiatement) des choses :
En rouge : la seed du PRNG (Pseudo Random Number Generator) est définie par l’heure à laquelle le script est exécuté
En bleu : la valeur de seed (cur_time) est concaténée au message à chiffrer En vert : cur_time n’est pas xorée via les nombres aléatoires mais par 0x88 Il fallait maintenant savoir que :
-
Python utilise l’algorithme de Mersenne Twister pour la fonction random.random() (qui n’est pas un CryptographicallySecurePRNG)
-
La sortie de ce PRNG est conditionnée par son état (un état donne toujours la même sortie)
-
L’état du PRNG est défini par la seed
Pour récapituler : si on trouve la seed utilisée pour générer les nombres aléatoires alors on sera à notre tour capable de les générer à l’identique. Or comme on le remarque en bleu et vert c’est tout à fait possible, notre seed étant situé à la fin du message :
En python 3 len(str(time.time())) est égale à 18 ou 17. On a ainsi donc nos bytes qui une fois xorés avec 0x88 nous redonnent le timestamp : 1513719133.8728752 (le 19/12/2017 à 22:32:13)
0x2 Scripting
Bon on possède tout les ingrédients, reste qu’à développer un script qui va générer les nombres aléatoires grâce au seed trouvé puis effectuer l’opération inverse. C’est à dire un xor entre chaque bytes du fichier et un des nombres aléatoires (putain oui j’adore python):
Here is your flag: 34C3_otp_top_pto_pot_tpo_opt_wh0_car3s
Notes importantes :
- La génération des nombres aléatoires diffère selon les versions de python : on obtient une suite différente avec python34 et python27 malgré une même seed. Je ne sais pas encore pourquoi, mon hypothèse est un changement dans l’implémentation de l’algorithme
- random.seed(“1513719133.8728752”) != random.seed(“1513719133.8728752”.encode(“ASCII”)), oui c’est con et j’ai perdu du temps à cause de ça ..
Références :