Un article qui décrit l’ algorithme md5crypt qui est toujours
- utilisé par les IOS CISO (secret 5)
Exemple: $1$28772684$iEwNOgGugqO9.bIz5sk8k/ - proposé par htpassword. Option -m
Exemple: $apr1$VaN8AS0b$Z7hfqYrZ/.yOuxQepKEHe1 - utilisé dans /etc/shadow (les distributions récentes n’ utilisent plus md5crypt mais sha512crypt).
Exemple : $1$28772684$iEwNOgGugqO9.bIz5sk8k/
Algorithme
Le mot de passe et le « salt » sont fournis. Le « salt » est tronqué à 8 caractères
Calcul de Hash_A
On calcule un hash md5 selon l’algo suivant. je le nomme Hash_A. Le résultat est au format digest(raw).
|| signifie concaténation
hash_A = md5(password || salt || password)
Calcul de Hash_B
On calcule un deuxième hash md5 selon l’algo suivant. je le nomme Hash_B
Hash_A est utilisé pour calculer Hash_B.
Hash_B = md5(
password
||
$1$
||
salt
||
Hash_A (pour chaque bloc de 16 octets composant le password.
Si le password est inférieur à 16 octets, on passe a l’ étape suivante.
||
N octets de Hash_A pour les N octets restant du password.
||
représentation binaire de la longueur du password, en commençant par le bit de poids faible
si le bit est égale à 1 , on ajoute le premier caractère du password
si le bit est égale à 0 , on ajoute la valeur \x00 (null)
)
Calcul de Hash_C
Hash_B est utilisé pour calculer Hash_C.
Initialisation: Context_C est vide
Pour 1000 itérations (indice de 0 à 999 inclus)
si indice est impair alors Context_C = Context_C || password
si indice est pair
à l’itération 0 alors Context_C = Context_C || Hash_B
aux itération paires suivantes, Context_C = Context_C || (Hash_C précédent)
si l’indice n’est pas un multiple de 3 , Context_C = Context_C || salt
(pour savoir si c’est un multiple de 3, il suffit d’ utiliser la fonction modulo 3)
si l’indice n’est pas un multiple de 7 , Context_C = Context_C || password
(pour savoir si c’est un multiple de 7, il suffit d’utiliser la fonction modulo 7)
si l’indice est pair , Context_C = Context_C || password
si l’indice est impair
à l’itération 1 alors Context_C = Context_C || Hash_B
aux itérations impaires suivantes, Context_C = Context_C || (Hash_C précédent)
si indice est pair, Context_C = Context_C || password
Hash_C = md5 (Context_C)
Transposition et encodage base64 du Hash_C
Hash_C : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 12, 13, 14, 15
Transposition : 0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 5, 11
Découpage en groupe de 3 octets:
- le premier groupe de 3 octets : position 0,6,12 du Hash_C
- le deuxième groupe de 3 octets : position 1,7,13 du hash_C
- le troisième groupe de 3 octets : position 2,8,14 du Hash_C
- le quatrième groupe de 3 octets : position 3,9,15 du Hash_C
- le cinquième groupe de 3 octets : position 4,10,5 du hash_C
Encodage base 64 des 16 octets en 22 caractéres (ajout de 4 bits de bourrage égales à 0)
Modèle d’encodage base64 :
1 2 3 4 5 6
0123456789012345678901234567890123456789012345678901234567890123
./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Pour le premier groupe de 3 octets
octet 0 0octet 6 0octet 12
aaaabbBB ccccCCCC DDddeeee
Découpage en 4 blocs de 6 bits
1er bloc : ddeeee
2ième bloc : CCCCDD
3ième bloc : BBBcccc
4ieme bloc : aaaabb
Même principe pour les 4 groupes de 3 octets suivants.
Pour le dernier octet
octet 11
AAaabbbb
Découpage en 2 blocs de 6 bits (bourrage à gauche avec 4 bits à 0)
(0000)AA aabbbb
1er bloc : aabbbb
2ieme bloc : 0000AA
Programme python
Pour finir un programme en python qui calcule un hash md5crypt
Vous devez fournir le salt et le password. Lignes [94,95] et [103,104].
Pour sélectionner tout le code , double-cliquez dans la fenêtre .
# -*- coding: cp1252 -*-
import hashlib
MAGIC = '$1$' # Magic string
ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
def to64 (v, n):
ret = ''
while (n - 1 >= 0):
n = n - 1
ret = ret + ITOA64[v & 0x3f]
v = v >> 6
return ret
def apache_md5_crypt (pw, salt):
return unix_md5_crypt(pw, salt, '$apr1$')
def unix_md5_crypt(pw, salt, magic=None):
if magic==None:
magic = MAGIC
if salt[:len(magic)] == magic:
salt = salt[len(magic):]
# le "salt" au maximum 8 caractères
import string
salt = string.split(salt, '$', 1)[0]
salt = salt[:8]
ctx = pw + magic + salt
# hash_A
final = hashlib.md5(pw + salt + pw).digest()
for pl in range(len(pw),0,-16):
if pl > 16:
ctx = ctx + final[:16]
else:
ctx = ctx + final[:pl]
# ctx = password + magic + salt + raw_hash_final (nombre de caractère du password)
i = len(pw)
while i:
if i & 1:
ctx = ctx + chr(0)
else:
ctx = ctx + pw[0]
i = i >> 1
# si le premier de la longueur (i) est à 1 , on ajoute \x00
# si le premier de la longueur (i) est à 0 , on ajoute la première lettre du mot de passe
# print hash_B
final = hashlib.md5(ctx).digest()
# itération pour lutter contre le bruteforce.
for i in range(1000):
ctx1 = ''
if i & 1:
ctx1 = ctx1 + pw
else:
ctx1 = ctx1 + final[:16]
if i % 3:
ctx1 = ctx1 + salt
if i % 7:
ctx1 = ctx1 + pw
if i & 1:
ctx1 = ctx1 + final[:16]
else:
ctx1 = ctx1 + pw
final = hashlib.md5(ctx1).digest()
passwd = ''
passwd = passwd + to64((int(ord(final[0])) << 16) |(int(ord(final[6])) << 8) |(int(ord(final[12]))),4)
passwd = passwd + to64((int(ord(final[1])) << 16) |(int(ord(final[7])) << 8) |(int(ord(final[13]))),4)
passwd = passwd + to64((int(ord(final[2])) << 16) |(int(ord(final[8])) << 8) |(int(ord(final[14]))),4)
passwd = passwd + to64((int(ord(final[3])) << 16) |(int(ord(final[9])) << 8) |(int(ord(final[15]))),4)
passwd = passwd + to64((int(ord(final[4])) << 16) |(int(ord(final[10])) << 8)|(int(ord(final[5]))),4)
passwd = passwd + to64((int(ord(final[11]))), 2)
return magic + salt + '$' + passwd
if __name__ == "__main__":
# mot de passe : hashcat - hash : $1$28772684$iEwNOgGugqO9.bIz5sk8k/
# unix et cisco
# arguments
password="hashcat"
salt="28772684"
print "unix ou cisco"
# arg01: password
# arg02: salt
print unix_md5_crypt(password, salt)
# apache
# mot de passe : hashcat - hash : $apr1$71850310$gh9m4xcAn3MGxogwX/ztb.
# arguements
password="hashcat"
salt="71850310"
print "apache"
# arg01: password
# arg02: salt
print apache_md5_crypt(password,salt)