Chiffrement des mots de passe Netscreen (3/3) - Analyse de la fonction de chiffrement et cassage des mots de passe

Thu 03 January 2008 by JB

On sait ce qu'on cherche, mais le programme est relativement gros : par oùcommencer ? On trouve facilement plusieurs fonctions de hachage, et mêmeplusieurs MD5, qui doivent provenir de différentes bibliothèques. En outreil n'est pas certain que la fonction de hachage utilisée soit un MD5.

Suppression des caractères fixes

La certitude est la présence de caractères fixes. On peut chercher la procédurequi les rajoute. Il doit y avoir des instructions du type :

MOV     Rx, #0x6E ; 0x6E = 'n'
STRB    Rx, [Ry]

On fait donc une recherche de la chaîne ", #0x6E" dans le listing. Laseconde occurrence est celle qui nous intéresse :

ROM:000E80D4             sub_E80D4                               ; CODE XREF: sub_E80AC+C
ROM:000E80D4 E3 A0 30 6E                 MOV     R3, #0x6E       ; 'n'
ROM:000E80D8 E5 C1 30 00                 STRB    R3, [R1]
ROM:000E80DC E5 D0 C0 00                 LDRB    R12, [R0]
ROM:000E80E0 E5 C1 C0 01                 STRB    R12, [R1,#1]
...
ROM:000E8104 E3 A0 C0 72                 MOV     R12, #0x72      ; 'r'
ROM:000E8108 E5 C1 C0 06                 STRB    R12, [R1,#6]
ROM:000E810C E5 D0 C0 05                 LDRB    R12, [R0,#5]
ROM:000E8110 E5 C1 C0 07                 STRB    R12, [R1,#7]
...
ROM:000E8134 E3 A0 C0 63                 MOV     R12, #0x63      ; 'c'
ROM:000E8138 E5 C1 C0 0C                 STRB    R12, [R1,#0xC]
ROM:000E813C E5 D0 C0 0A                 LDRB    R12, [R0,#0xA]
ROM:000E8140 E5 C1 C0 0D                 STRB    R12, [R1,#0xD]

Cette fonction copie la chaîne de caractères à l'adresse R0 vers l'adresse R1, en rajoutant les caractères n, r, c, s, t et n à des endroits fixés. Appelons là EncodePassword.

Juste en dessous se trouve la procédure qui enlève ces caractères, DecodePassword.

Hachage du mot de passe

On peut supposer que toutes les fonctions relatives au stockage des mots de passese trouvent dans le même fichier source (sauf les fonctions réutilisables, commele MD5). Ces fonctions seront alors situées dans la même zone une fois compilées.

Il ne faut pas chercher très loin pour localiser la procédure recherchée :la fonction ComputePassword, au-dessus de EncodePassword, est la bonne.Le graphe d'appel de la fonction ComputePassword est très petit. Le rôlede toutes les fonctions est réellement facile à déterminer, notamment en regardantles chaînes de caractères utilisées dans chacune des procédures.

  • 0x67452301, 0xEFCDAB89, 0x98BADCFE et 0x10325476 dans la fonction MD5Init ;
  • 0xD76AA478, 0xE8C7B756, etc. dans la fonction MD5Compress ;
  • la table de caractères base 64 dans la fonction EncodeBase64.

callgraph.png Fig. 3 - Graphe d'appel de la fonction ``ComputePassword``

Le premier appel de cette fonction est une initialisation d'un MD5 :

ROM:007D53E0             MD5Init_Pwd
ROM:007D53E0
ROM:007D53E0 E5 9F C0 2C                 LDR     R12, =0x67452301
ROM:007D53E4 E5 80 C0 00                 STR     R12, [R0]
ROM:007D53E8 E5 9F C0 28                 LDR     R12, =0xEFCDAB89
ROM:007D53EC E5 80 C0 04                 STR     R12, [R0,#4]
ROM:007D53F0 E5 9F C0 24                 LDR     R12, =0x98BADCFE
ROM:007D53F4 E5 80 C0 08                 STR     R12, [R0,#8]
ROM:007D53F8 E5 9F C0 20                 LDR     R12, =0x10325476
ROM:007D53FC E5 80 C0 0C                 STR     R12, [R0,#0xC]
ROM:007D5400 E3 A0 C0 00                 MOV     R12, #0
ROM:007D5404 E5 80 C0 10                 STR     R12, [R0,#0x10]
ROM:007D5408 E5 80 C0 14                 STR     R12, [R0,#0x14]
ROM:007D540C E5 80 C0 58                 STR     R12, [R0,#0x58]
ROM:007D5410 E1 A0 F0 0E                 RET
ROM:007D5410             ; End of function MD5Init_Pwd

Viennent ensuite cinq appels à MD5Update_Pwd, avec enparamètres :

  • le nom de l'utilisateur ;
  • la chaîne : ;
  • la chaîne Administration Tools ;
  • la chaîne : ;
  • le mot de passe de l'utilisateur.

Le hash est calculé avec la fonction MD5Finish_Pwd,puis est converti en base 64 par blocs de 16 bits avecla fonction EncodeBase64.

Les condensats sont donc des MD5 du mot de passe avec un sel formédu nom d'utilisateur suivi d'une chaîne de caractères fixe.

Voilà un exemple avec le couple netscreen / netscreen.

MD5(\"netscreen:Administration Tools:netscreen\")=A554CDB0C533FAE46E5681DD2AA4089B

Chaque bloc de 16 bits est encodé en base 64 :

A554 CDB0 C533 FAE4 6E56 81DD 2AA4 089B
KVU  M2w  MUz  Prk  G5W  IHd  Cqk  Aib

Et les caractères fixes sont ensuite ajoutés : nKVUM2rwMUzPcrkG5sWIHdCtqkAibn

On obtient le même mot de passe que dans la section Observation.Notre analyse est donc correcte.

Casser les mots de passe

Un plugin a ensuite été écrit pour John the Ripper. Le sel est de taille variable, contrairement aux autres algorithmes présents dans John the Ripper. De plus, la taille du sel est trop importante pour la routine MD5 optimisée déjà implémentée. Une autre implémentation a été utilisée.

La vitesse de l'algorithme est relativement faible par rapport à un simple MD5 sans sel, qui utilise des fonctions bien optimisées. Malgré tout environ 2 millions de mots de passe par seconde sont testés sur un Intel Core Duo. On peut espérer casser quelques mots de passe pas trop compliqués :

admin:admin!nMjFM0rdC9iOc+xIFsGEm3LtAeGZhn
frank:frank!nE8aAXr/DA+IcULCJszP9mFtT1AK9n
admin:admin!nPPhH5rpMZSLcXlBTsMOG/DtliOwqn
jb:jb!nH0/OLrJHTxHch5I0sBFfHJtTkDZKn
netscreen:netscreen!nN7zP+rCPTpCctqPUssBm+ItXoNp0n

John the Ripper fournit quelques résultats rapidement :

$ ./john.exe ../../netscreen/pass.txt
Loaded 5 password hashes with 4 different salts (Juniper ScreenOS [ns-md5])
password         (frank)
password         (admin)
12345678         (netscreen)
jb123            (jb)
guesses: 4  time: 0:00:03:10 (3)  c/s: 1615K  trying: sui1156
Session aborted

Il est également envisageable de calculer des rainbow tables pour casser les mots de passe. Le sel n'est dépendant que du nom d'utilisateur. Or il se trouve que, dans la pratique, les noms couramment utilisés sont soit netscreen, soit admin. On peut créer des tables pour ces deux noms d'utilisateurs pour casser, en ayant une puissance de calcul relativement importante, les mots de passe alpha-numériques jusqu'à 8 caractères.

Code source : encodage des mots de passe

La fonction GetPasswordHash encode les mots de passe NetScreen et affiche le résultat. Les caractères fixes ne sont pas insérés.

int GetPasswordHash(char *login, char *password)
{
    static const char b64charset[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    static unsigned char adm_tools[] = ":Administration Tools:";
    unsigned char digest[16];
    char b64digest[25];
    int i, j;
    unsigned short s;

    if(!login || !password)
        return -1;
    /* Calcule le hash MD5 de login + ":Administration Tools:" + pass */
    MD5_CTX ctx;
    MD5Init(&ctx);
    MD5Update(&ctx, (unsigned char *)login, (int)strlen(login));
    MD5Update(&ctx, (unsigned char *)":Administration Tools:",
        (int)strlen(adm_tools));
    MD5Update(&ctx, (unsigned char *)password, (int)strlen(password));
    MD5Final(digest, &ctx);

    /* Encodage base 64 par blocs de 3 octets */
    for (i = 0; i < 8; i++) {
        s = ((unsigned short *)digest)[i];
        /* Conversion big endian -> little endian */
        s = ((s & 0xff) << 8) | (s >> 8);

        for(j = 0; j < 3; j++) {
            b64digest[3 * i + 2 - j] = b64charset[s & 0x3f];
            s >>= 6;
        }
    }
    b64digest[24] = 0;
    printf("%s\n", b64digest);
    return 0;
}