**Reverse 250 - unVM me** Énoncé : If I tell you what version of python I used .. where is the fun in that? On lance le challenge, dans l'enoncé ça parle de python, pas trop surpris de voir : **unvm_me.pyc** comme fichier. L'extension est .pyc, c'est du python compilé. Première chose, on essaye de le décompiler pour afficher le code source en clair. Pour ça on peut utiliser [[https://github.com/Mysterie/uncompyle2|cet outil]]. Une fois que le .pyc est décompilé on l'ouvre avec notepad et on peut voir le code source en clair : # Embedded file name: unvm_me.py import md5 md5s = [174282896860968005525213562254350376167L, 137092044126081477479435678296496849608L, 126300127609096051658061491018211963916L, 314989972419727999226545215739316729360L, 256525866025901597224592941642385934114L, 115141138810151571209618282728408211053L, 8705973470942652577929336993839061582L, 256697681645515528548061291580728800189L, 39818552652170274340851144295913091599L, 65313561977812018046200997898904313350L, 230909080238053318105407334248228870753L, 196125799557195268866757688147870815374L, 74874145132345503095307276614727915885L] print 'Can you turn me back to python ? ...' flag = raw_input('well as you wish.. what is the flag: ') if len(flag) > 69: print 'nice try' exit() if len(flag) % 5 != 0: print 'nice try' exit() for i in range(0, len(flag), 5): s = flag[i:i + 5] if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5]: print 'nice try' exit() print 'Congratz now you have the flag' A partir de la on essaye de regrouper les informations disponibles pour comprendre comment arriver au flag. Deux premières conditions sont assez évidentes : * On voit que le flag ne peut pas faire plus de 69 caractères. * La taille du flag doit être un multiple de 5. La troisième condition est un peu plus compliquée, on va regarder le code par étape pour comprendre ce qu'il se passe. __Etape 1 :__ for i in range(0, len(flag), 5) Boucle for avec range à trois arguments. On va itérer selon la taille de la chaine de caractères du flag par pas de 5. On comprend mieux pourquoi il fallait un multiple de 5. __Etape 2 :__ s = flag[i:i + 5] Définition d'une variable **s** La variable **s** va être égale au 5 caractères en partant de la où se trouve l'itération **i** (on se rappelle qu'on itère en pas de 5). __Etape 3 :__ if int('0x' + md5.new(s).hexdigest(), 16) != md5s[i / 5] La dernière condition. On voit qu'ici on va convertir une string de format hexadécimal en int, c'est pour ça qu'on utilise l'argument 16 qui correspond à la base hexa pour la conversion. Cette string qui va être convertie c'est le md5 au format hexadécimal des 5 caractères récupérés dans **s** juste avant. Enfin cet int, doit être égal à celui correspondant dans la grosse liste du début avec des nombres de type long. __En résumé :__ Notre flag va être découpé en section de 5 caractères et pour chaque section le md5 de ces 5 caractères convertis en int de base 16 doit être égal à celui dans la liste correspondant (md5s). Chaque long dans la liste **md5s** correspond donc à 5 caractères du flag. Exemple pour mieux comprendre : s = 'abcde' p = md5.new(s).hexdigest() = 'ab56b4d92b40713acc5af89985d4b786' int('0x' + p, 16) = 227748192848680293725464448333830731654L Il faut donc faire la route inverse, on se renseigne sur comment convertir un int en string en python de préférence et on trouve une fonction sympa : def digit_to_char(digit): if digit < 10: return str(digit) return chr(ord('a') + digit - 10) def str_base(number,base): if number < 0: return '-' + str_base(-number, base) (d, m) = divmod(number, base) if d > 0: return str_base(d, base) + digit_to_char(m) return digit_to_char(m) On va donc itérer dans la liste et afficher toutes les strings md5 à partir des longs, ce qui donne : for n in md5s: print str_base(n, 16) On obtient ça : 831daa3c843ba8b087c895f0ed305ce7 6722f7a07246c6af20662b855846c2c8 5f04850fec81a27ab5fc98befa4eb40c ecf8dcac7503e63a6a3667c5fb94f610 c0fd15ae2c3931bc1e140523ae934722 569f606fd6da5d612f10cfb95c0bde6d 68cb5a1cf54c078bf0e7e89584c1a4e c11e2cd82d1f9fbd7e4d6ee9581ff3bd 1df4c637d625313720f45706a48ff20f 3122ef3a001aaecdb8dd9d843c029e06 adb778a0f729293e7e0b19b96a4c5a61 938c747c6a051b3e163eb802a325148e 38543c5e820dd9403b57beff6020596d On sait qu'un md5 fait forcement 32 caractères, on remarque à la ligne 7 : 68cb5a1cf54c078bf0e7e89584c1a4e qui ne fait que 31 caractères, durant la convertion on a perdu un 0 devant. **0**68cb5a1cf54c078bf0e7e89584c1a4e correspond au bon md5. On balance ça sur un [[https://hashkiller.co.uk/md5-decrypter.aspx|site]] pour casser les md5 et on se retrouve avec : ALEXCTF{dv5d4s2vj8nk43s8d8l6m1n5l67ds9v41n52nv37j481h3d28n4b6v3k}