Crypt : le chiffrement par défaut

Comme vous le savez sans doute, le système d'authentification utilisé lors de l'arrivée d'un utilisateur a recours à un chiffrement de base. Ainsi, les mots de passe ne se trouvent jamais "en clair" dans un fichier.

Si vous jetez un oeil dans le répertoire /etc, vous allez trouver les fichiers passwd (informations sur les comptes utilisateur) et shadow (le fichier des mots de passe) contenant respectivement des lignes comme celles-ci :

denis:x:500:0::/home/denis:/bin/bash
denis:p7wDKSadeAaWM:10917:0:10000::::

La première ligne provenant de passwd informe le système de l'existence de l'utilisateur "denis". La syntaxe du fichier est la suivante :

compte:pass:ID utilisateur:ID groupe:vrai nom:rep. home:shell

Dans le cas présent, le système utilise le shadow password et le mot de passe n'est donc pas pris en compte dans ce fichier mais dans /etc/shadow. shadow respecte la syntaxe suivante :

compte:pass:ancienneté:jours_restant:durée_validité:alarme:restant_avant_désactivation:désactivation:réservé

Ici, quelques explications s'imposent :

  • ancienneté contient la date du dernier changement en jours depuis le 1er Janvier 1970.
  • jours_restant contient le nombre de jours avant que l'utilisateur ait l'obligation de changer le mot de passe.
  • durée_validité détermine le nombre de jours durant lequel un mot de passe est valide.
  • alarme contient le nombre de jours avant la date d'expiration du compte pour avertir l'utilisateur.
  • restant_avant_désactivation contient le nombre de jours restant avant désactivation du compte.
  • désactivation est la date de désactivation du compte en jours depuis le 1er Janvier 1970.

    Ce qui nous intéresse ici est, bien sûr le champ "pass". Celui-ci contient le mot de passe crypté de l'utilisateur.

    Crypt()

    Sous ce nom se cache la fonction de chiffrement utilisée pour le stockage des mots de passe. La méthode utilisée est dite à sens unique car il est mathématiquement impossible d'obtenir le mot de passe en clair à partir de son résultat crypté. Lorsqu'un utilisateur s'identifie sur le système, il doit ensuite s'authentifier (prouver sa bonne fois) en donnant son mot de passe. Ce mot en clair est ensuite crypté et comparé avec l'enregistrement dans le fichier shadow. Si les deux valeurs sont identiques, l'utilisateur est bien celui qu'il prétend être, sinon son accès est refusé. La fonction crypt est basée sur un standard très courant; il s'agit de l'algorithme DES (Data Encryption Standard). crypt s'utilise de la manière suivante :

    char *crypt(const char *key, const char *salt);
    

    key est la chaîne de caractères "en clair" (habituellement le mot de passe). salt est une chaîne de deux caractères servant à perturber l'algorithme de chiffrement. La fonction crypt retourne un pointeur vers la chaîne de caractères chiffrée. Les deux premiers caractères de la chaîne chiffrés sont ceux de salt.

    Faiblesse

    Cette fonction ne saurait être utilisée pour chiffrer autre chose que des mots de passe. En effet, elle ne permet que 7.2e16 valeurs possibles. Comme nous l'avons dit, trouver un mot de passe à partir de la chaîne chiffrée est impossible. En revanche, essayer 7.2e16 mots de passe est à la portée du premier gros cluster. De plus, faire tous ces essais n'est pas forcément nécessaire. Certains logiciels comme Crack ou John the ripper utilisent une base de données pour faire les essais. Cette base contient des mots de passe courrament utilisés comme des noms d'objets, des prénoms, etc. En connaissant les goûts et l'entourage d'un utilisateur, on arrive dans bien des cas à trouver son mot de passe (lorsqu'il n'est pas tout simplement inscrit sous le clavier :-)

    Une version simpliste d'un tel logiciel d'attaque brutale tient en quelques lignes de C :

    #include <unistd.h>
    #include <stdio.h>
    #define FICHIER "fichiertest"
    int main(int argc, char *argv[]) {
     char *chiffre;
     char *salt;
     char lignefichier[256];
     FILE *monfile;
     if (argc<3) {
       printf("Usage : cryptest mot_code\n");
       exit(1);
       }
     chiffre=argv[1];
     salt=argv[2];
     monfile=fopen(FICHIER,"r");
     if(monfile == NULL) {
       printf("\nArgh ! Erreur fichier\n\n");
       exit(1);
       }
     while(fgets(lignefichier,sizeof(lignefichier)-1, monfile) != NULL) {
       if(lignefichier[strlen(lignefichier) - 1] == '\n')
         lignefichier[strlen(lignefichier) - 1] = '\0';
       if (strcmp(crypt(lignefichier,salt), chiffre)==0)
         printf("c'est %s\n",lignefichier);
       }
     fclose(monfile);
     return(0);
    }
    

    Linux Magazine France n°13 - Janvier 2000