**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}