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 :

  1. Python utilise l’algorithme de Mersenne Twister pour la fonction random.random()  (qui n’est pas un CryptographicallySecurePRNG)
  2. La sortie de ce PRNG est conditionnée par son état (un état donne toujours la même sortie)
  3. 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 :