web-dev-qa-db-fra.com

Quel est le 6e caractère du hachage de mot de passe sous Linux, et pourquoi est-ce souvent une barre oblique?

Sous Linux, quel est le sixième caractère du hachage de mot de passe stocké dans /etc/shadow?

Sur ma boîte Linux style chiot, si j'essaie de générer 100 mots de passe aléatoires en utilisant shuf et /dev/urandom, le sixième caractère est / environ la moitié du temps.

Ma question n'est pas à des fins de production, car je le démarre à chaque fois à partir d'un CD. Cela signifie-t-il que mon système est mal configuré ou non sécurisé d'une manière ou d'une autre?

J'ai exécuté le fichier sur shuf pour voir s'il s'agissait d'un lien busybox.

file /usr/bin/shuf

    shuf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, stripped

Je ne pense pas que shuf soit un lien busybox ici.

ls -l /usr/bin/shuf

    -rwxr-xr-x 1 root root 41568 Mar  7  2015 /usr/bin/shuf

tandis que

ls -l /bin/wget

    lrwxrwxrwx 1 root root 14 Apr 29 03:49 wget -> ../bin/busybox

Voici une idée approximative de ce que j'ai fait:

# ! / b i n / b a s h
##  don't try this on any real computer
##  this is not a production script, it is just psuedo code
##  with pseudo results to illustrate a point

##  for this run of 100 ?random? passwords,
##  46 of the 6th character of the hash stored in
##  '/ect/shadow' were '/'

function is_this_really_a_random_password () {
PERHAPS_RANDOM=''
for (( Z=0 ; Z<=8 ; Z++ )) do
PERHAPS_RANDOM="$PERHAPS_RANDOM$( shuf --head-count=1 --random-source=/dev/urandom $FILE_OF_SAFE_CHARACTERS )"
done
echo "$USER_NAME:$PERHAPS_RANDOM" | chpasswd
}

rm sixth-character-often-forward-slash.txt
for (( I=1; I<=100; I++ )) do
is_this_really_a_random_password
grep --regexp=root /etc/shadow | cut --characters=-40 >> sixth-character-often-forward-slash.txt
done
    root:$5$56YsS//DE$HasM6O8y2mnXbtgeE64zK
    root:$5$ho8pk/4/A6e/m0eW$XmjA5Up.0Xig1e
    root:$5$jBQ4f.t1$vY/T/1kX8nzAEK8vQD3Bho
    root:$5$BJ44S/Hn$CsnG00z6FB5daFteS5QCYE
    root:$5$Jerqgx/96/HlV$9Wms5n1FEiM3K93A8
    root:$5$qBbPLe4zYW$/zXRDqgjbllbsjkleCTB
    root:$5$37MrD/r0AlIC40n6$8hplf2c3DgtbM1
    root:$5$.4Tt5S6F.3K7l7E$dAIZzFvvWmw2uyC
    root:$5$A4dX4ZlOoE$6axanr4GLPyhDstWsQ9B
    root:$5$HXAGhryJ/5$40tgmo7q30yW6OF7RUOE
    root:$5$EzNb9t5d$/nQEbEAQyug7Dk9X3YXCEv
    root:$5$HHS5yDeSP$LPtbJeTr0/5Z33vvw87bU
    root:$5$sDgxZwTX5Sm$6Pzcizq4NcKsWEKEL15
    root:$5$FK1du/Paf/$hAy8Xe3UQv9HIpOAtLZ2
    root:$5$xTkuy/BLUDh/N$/30sESA.5nVr1zFwI
    root:$5$PV4AX/OjZ$VU8vX651q4eUqjFWbE2b/
    root:$5$iDuK0IUGijv4l$cdGh8BlHKJLYxPB8/
    root:$5$0DEUp/jz$JBpqllXswNc0bMJA5IFgem
    root:$5$Wz3og/W3Jra/WKA.$6D7Wd4M1xxRDEp
    root:$5$ntHWB.mC3x$Kt4DNTjRZZzpbFvxpMxP
    root:$5$g/uEc/cq$Ptlgu8CXV.vrjrmuok9RRT
    root:$5$/XAHs/5x$Z9J4Zt4k6NxdjJ27PpLmTt
    root:$5$mgfbZeWD0h/$UDGz8YX.D85PzeXnd2K
    root:$5$f4Oh3/bF2Ox/eN$xt/Jkn0LxPnfKP8.
    root:$5$J0mZZXGJG7/v$e16VxghNvZZKRONown
    root:$5$SNza9XFl9i$Qq7r/N6Knt2j74no8H0x
    root:$5$aFCu//xiL$Ocn9mcT2izcnm3rUlBOJg
    root:$5$kMkyos/SLZ/Mm6$wNYxZ9QeuJ8c8T.o
    root:$5$ujXKC/Xnj0h/nQ$PUmePvJZr.UXmTGK
    root:$5$wtEhA/YKaTKH$6VCSXUiIdsfelkCYWV
    root:$5$I1taRlq59YZUGe$4OyIfByuvJeuwsjM
    root:$5$N54oH//j4nbiB$K4i6QOiS9iaaX.RiD
    root:$5$ps8bo/VjPGMP0y4$NTFkI6OeaMAQL7w
    root:$5$IRUXnXO8tSykA8$NatM5X/kKHHgtDLt
    root:$5$VaOgL/8V$m45M9glUYnlTKk8uCI7b5P
    root:$5$/lPDb/kUX73/F3$jJL.QLH5o9Ue9pVa
    root:$5$/sHNL/tVzuu//cr$QasvQxa02sXAHOl
    root:$5$hGI.SMi/7I$fYm0rZP0F5B2D1YezqtX
    root:$5$WsW2iENKA$4HhotPoLRc8ZbBVg4Z5QW
    root:$5$cN6mwqEl$q5S3U85cRuNHrlxS9Tl/PC
    root:$5$wwzLR/YMvk5/7ldQ$s3BJhq5LyrtZww
    root:$5$GUNvr/d15n8/K$CiNHwOkAtxuWJeNy1
    root:$5$nGE75/8mEjM/A$pD/84iLunN/ZNI/JK
    root:$5$77Dn2dHLS$d5bUQhTz.OU4UA.67IGMB
    root:$5$EWrI//1u$uubkPk3YhAnwYXOYsvwbah
    root:$5$Hzfw1UCudP/N/U$Rjcdzdbov1YgozSJ
    root:$5$2y8CKTj.2eTq$7BEIgMWIzAJLl1SWBv
    root:$5$lcWsD/42g8zEEABA$r/vGxqqUZTkJ0V
    root:$5$LPJLc/Xz$tnfDgJh7BsAT1ikpn21l76
    root:$5$ucvPeKw9eq8a$vTneH.4XasgBIeyGSA
    root:$5$Fwm2eUR7$ByjuLJRHoIFWnHtvayragS
    root:$5$yBl7BtMb$KlWGwBL6/WjgHVwXQh9fJS
    root:$5$1lnnh2kOG$rdTLjJsSpC3Iw4Y6nkPhq
    root:$5$WfvmP6cSfb066Z$1WvaC9iL11bPCAxa
    root:$5$qmf/hHvalWa4GE25$m3O2pdu25QBCwU
    root:$5$4P.oT/9HQ$Ygid4WXi0QCEObLVNsqFZ
    root:$5$FNr4Bkj56Y$38mG7mKV0mdb1PMCxrVd
    root:$5$hoNcyURtV$aTidBWHjngc1I0vUTi5bB
    root:$5$rzHmykYT$ATiXdUDUvUnB2fNMUQgwvE
    root:$5$o11Yb/ZQv2/k3wg9$5yShpVejDBk6HB
    root:$5$REPGN//y9H$awpPmUvCqvi6Bd/6bQxF
    root:$5$HbAEY/djXJx$y56GhMwavd7xTQ.jPg6
    root:$5$3T1k5.LZUcy$Cup.LM5AnaBTIaJtBnF
    root:$5$wXaSC/P8bJ$y/0DoYJVjaP09O6GWiki
    root:$5$YuFfY8QPqm/dD$IIh0/tyn.18xEBl5Y
    root:$5$uTTBpjsKG//3Et8$9ibN9mVwSeVyOI4
    root:$5$dASlMLzbVbFMnZ$N4uGBwGHhdg93z/V
    root:$5$03.FA/LnRBb.k7Zl$XOHU2ZlHkV9oz9
    root:$5$2zL1p/VDCi$/QRT7Bo3cZ3Rxb8Y7ddo
    root:$5$0NpZqZs/qt/jIv.$8W/TTM3Gy2UMOWy
    root:$5$a4SXynoro7ucT$qFM2C79QJ15jQ0ZlL
    root:$5$RL0Eg/jroH8/ONP$EzceXz.pz74k104
    root:$5$O3R5V/n1$U.mmCTbpID8xMXbvtzd4ch
    root:$5$0T2nVrv/P/xaRwUD$YVm17XF8kTsL0f
    root:$5$2bRwMNIXobZwn$Q228FJqg6/iRCe9GQ
    root:$5$PyYgL/axfgj/$uaL5y/kdzU4Kzi.JlB
    root:$5$A6QtfJdJ4Gwvx4$d4PA5AJ0806NzRnm
    root:$5$H8Mta5LDgGXp$QGdOJh.bFWgR3L719Z
    root:$5$H06URjv4BtOAbA$EJs1mZYhdKIVgCmn
    root:$5$OeB.O/GrmFB/az$SoE759KE9WIE17Uf
    root:$5$huiB9/sk$el3XMf7SGX81LnD3.SaF8J
    root:$5$fO7tfM.fjdSHA8G6$s.QIjfNniCzFdU
    root:$5$32at3SQJAD/xlw$HbXmBLVXTTyZfxQv
    root:$5$FHBFL/QdFl$FMipxpW0HlEFUIAr7IxF
    root:$5$sHvKf/M5OPdBuZZ$dz4qLOkTLGeCINX
    root:$5$hw4Vu/e34$/82lXu7ISrse.Ihk.qbqT
    root:$5$k1JOy/jRWZ$30YSk7kbhdKOjfDaiWVf
    root:$5$MnX.LUzqrB/B2$JuwqC.SmKFnMUWkEf
    root:$5$arRYf/PG$Xw6PpZNFO656p.Eb636iLt
    root:$5$5op/p8Hqs5$Nj2jA0Qxm80aG4fHW3oz
    root:$5$VHIT9/8yzZ$CpIK4ODps78GcqcsgiMT
    root:$5$.AlH7jBJoh/8$sjuVt.PcRH.vyvB3og
    root:$5$f7Ewinqm$nrJ2p/hKTuiEK//IfCTjth
    root:$5$N.dv/VCvrCADg$peSXfo35KN1dmbw/n
    root:$5$PSc4W./54l/SroH$CFFVOHRYK.Jj8Sp
    root:$5$8UBP3f4IcnAd/N1/$P.ud49qTStQ7Lw
    root:$5$qnXsZ/NlLZh/$nlaQVTS3FCJg1Jb2QG
    root:$5$xOpbbBqENR/7$boYJQzkCkZhRf7Uicf
    root:$5$V93tjZhzT$LrsIZWZmYo4ocRUvCixO6
    root:$5$1MVz8/lf5oC/$rUKpnX23MhFx4.y2ZS

Environ la moitié des 6e caractères de hachage sont /:

cat sixth-character-often-forward-slash.txt | cut --character=14 | sort


    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    /
    .
    .
    .
    .
    2
    5
    6
    8
    8
    B
    d
    D
    e
    e
    E
    f
    H
    I
    j
    j
    j
    J
    k
    k
    K
    l
    L
    M
    M
    n
    n
    N
    q
    r
    r
    r
    s
    S
    S
    t
    t
    T
    U
    U
    U
    U
    V
    w
    x
    X
    X
    X
    Z
    Z
    Z
84
insecure

Format et source de hachage

Le format du hachage de mot de passe est $<type>$<salt>$<hash>, Où <type>5 Est un hachage basé sur SHA-256. Le sel contient généralement au moins 8 caractères (et se trouve dans les exemples de la question), le sixième caractère fait donc partie du sel.

Ces hachages sont probablement générés par une version de suite d'outils shadow (paquet src shadow dans Debian, shadow-utils dans CentOS)

J'ai essayé de savoir pourquoi, exactement, le code biaisait la barre oblique. (merci à @thrig d'avoir initialement trouvé le code.)

TLDR: C'est un peu intéressant, mais ça n'a pas d'importance.


Le code générant le sel

Dans libmisc/salt.c, Nous trouvons la fonction gensalt qui appelle l64a Dans une boucle:

strcat (salt, l64a (random()));
do {
       strcat (salt, l64a (random()));
} while (strlen (salt) < salt_size);

La boucle prend un nombre aléatoire de random(), le transforme en un morceau de chaîne et le concatène à la chaîne formant le sel. Répétez jusqu'à ce que suffisamment de personnages soient collectés.

Ce qui se passe dans l64a Est cependant plus intéressant. La boucle interne génère un caractère à la fois à partir de la valeur d'entrée (qui provient de random()):

for (i = 0; value != 0 && i < 6; i++) {
    digit = value & 0x3f;

    if (digit < 2) {
        *s = digit + '.';
    } else if (digit < 12) {
        *s = digit + '0' - 2;
    } else if (digit < 38) {
        *s = digit + 'A' - 12;
    } else {
        *s = digit + 'a' - 38;
    }

    value >>= 6;
    s++;
}

La première ligne de la boucle (digit = value & 0x3f) Sélectionne six bits dans la valeur d'entrée et les clauses if transforment la valeur formée par celles-ci en caractère. (. Pour zéro, / Pour un, 0 Pour deux, etc.)

l64a Prend un long mais les valeurs sorties par random() sont limitées à Rand_MAX, Qui semble être 2147483647 ou 2 ^ 31 - 1 sur glibc. Ainsi, la valeur qui va à l64a Est un nombre aléatoire de 31 bits. En prenant 6 bits à la fois ou une valeur de 31 bits, nous obtenons cinq caractères répartis de manière raisonnablement égale, plus un sixième qui ne provient que d'un bit!

Le dernier caractère généré par l64a Ne peut cependant pas être un ., Car la boucle a également la condition value != 0 Et au lieu d'un . Comme sixième caractère , l64a Ne renvoie que cinq caractères. Par conséquent, la moitié du temps, le sixième caractère est un / Et la moitié du temps l64a Renvoie cinq caractères ou moins. Dans ce dernier cas, un l64a Suivant peut également générer une barre oblique dans les premières positions, donc dans un sel complet, le sixième caractère devrait être une barre oblique un peu plus de la moitié du temps.

Le code a également une fonction pour randomiser la longueur du sel, c'est 8 à 16 octets. Le même biais pour le caractère barre oblique se produit également avec d'autres appels à l64a, Ce qui ferait que les 11e et 12e caractères auraient également une barre oblique plus souvent qu'autre chose. Les 100 sels présentés dans la question ont respectivement 46 barres obliques en sixième position et 13 et 15 en 11e et 12e position. (un peu moins de la moitié des sels ont moins de 11 caractères).

Sur Debian

Sur Debian, je ne pouvais pas reproduire cela avec un chpasswd droit comme indiqué dans la question. Mais chpasswd -c SHA256 Montre le même comportement. Selon le manuel, l'action par défaut, sans -c, Est de laisser PAM gérer le hachage, donc apparemment PAM sur Debian utilise au moins un code différent pour générer le sel. Cependant, je n'ai regardé le code PAM sur aucune distribution.

(La version précédente de cette réponse indiquait que l'effet n'apparaissait pas sur Debian. Ce n'était pas correct.)

Importance et exigences pour les sels

Mais est-ce important? Comme l'a commenté @RemcoGerlich, ce n'est quasiment qu'une question d'encodage. Cela fixera efficacement certains bits du sel à zéro, mais il est probable que cela n'aura aucun effet significatif dans ce cas, car l'origine de ces bits est cet appel à srandom dans seedRNG:

srandom (tv.tv_sec ^ tv.tv_usec ^ getpid ());

Ceci est une variante de la coutume que vous avez de semer un RNG avec l'heure actuelle. (tv_sec Et tv_usec Sont les secondes et les microsecondes de l'heure actuelle, getpid() donne l'ID du processus si le processus est en cours.) Comme l'heure et les PID ne sont pas très imprévisibles , la quantité d'aléatoire ici n'est probablement pas supérieure à ce que le codage peut contenir.

L'heure et le PID ne sont pas quelque chose que vous aimeriez créer touches avec, mais peuvent être assez imprévisibles pour les sels. Les sels doivent être distincts pour empêcher le test en force brute de plusieurs hachages de mot de passe avec un seul calcul, mais devrait être également imprévisible, pour empêcher ou ralentir le précalcul ciblé, qui pourrait être utilisé pour raccourcir le délai entre l'obtention des hachages de mot de passe et l'obtention des mots de passe réels.

Même avec les légers problèmes, tant que l'algorithme ne génère pas le même sel pour différents mots de passe, ça devrait aller. Et cela ne semble pas le cas, même lors de la génération de quelques dizaines dans une boucle, comme le montre la liste de la question.

De plus, le code en question n'est utilisé que pour générer des sels pour les mots de passe, il n'y a donc aucune implication sur les problèmes ailleurs.

Pour les sels, voir également, par ex. ceci sur Stack Overflow et ceci sur security.SE .

Conclusion

En conclusion, il n'y a rien de mal avec votre système. Il est plus utile de s'assurer que vos mots de passe sont bons et qu'ils ne sont pas utilisés sur des systèmes indépendants.

87
ilkkachu

Ce caractère fait partie du sel selon le manuel crypt(3). Étant donné que la longueur du sel (la chaîne entre les $5$ ID et suivants $) varie pour les hachages exposés, je ne sais pas exactement ce qu'illustre le choix d'un caractère aléatoire dans cette colonne particulière pour quelques mots de passe.

En revanche, le / est plutôt plus répandu (102 instances) dans le sel entier par rapport aux autres caractères possibles (environ 18), donc quelque chose dans chpasswd semble favoriser ce caractère dans le sel;

for x in `seq 1 100000`; do
  echo testacct:asdfasdfasdf | chpasswd -c SHA256
  awk -F: '/testacct/{print $2}' /etc/shadow | awk -F\$ '{print $3}' >> salts
done
Perl -nle 'print for m/(.)/g' salts | sort | uniq -c | sort -nr | head -5

sur un système RedHat EL 6, il se produit:

   1006 /
    195 X
    193 U
    193 q
    193 e

Et, oui, le code dans shadow-utils-4.1.5.1-5.el6 montre un biais envers / ce qui pourrait faciliter les attaques par dictionnaire:

#include <sys/time.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

// these next two borrowed from libmisc/salt.c of shadow-4.1.5.1 from
// Centos 6.8 RPM at http://vault.centos.org/6.8/os/Source/SPackages/shadow-utils-4.1.5.1-5.el6.src.rpm
char *l64a(long value)
{
    static char buf[8];
    char *s = buf;
    int digit;
    int i;

    if (value < 0) {
        abort();
    }

    for (i = 0; value != 0 && i < 6; i++) {
        digit = value & 0x3f;

        if (digit < 2) {
            *s = digit + '.';
        } else if (digit < 12) {
            *s = digit + '0' - 2;
        } else if (digit < 38) {
            *s = digit + 'A' - 12;
        } else {
            *s = digit + 'a' - 38;
        }

        value >>= 6;
        s++;
    }

    *s = '\0';

    return (buf);
}

static void seedRNG(void)
{
    struct timeval tv;
    static int seeded = 0;

    if (0 == seeded) {
        (void) gettimeofday(&tv, NULL);
        srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
        seeded = 1;
    }
}

int main(void)
{
    seedRNG();
    for (int x = 0; x < 1000; x++) {
        printf("%s\n", l64a(random()));
    }

    exit(0);
}

Ce qui se traduit par:

% ./salttest | Perl -nle 'print for m/(.)/g' | sort | uniq -c | sort -nr | head -3
 593 /
  96 8
  93 3

Et puis en utilisant les mêmes routines que les dernières https://github.com/shadow-maint/shadow/blob/master/libmisc/salt.c nous constatons qu'il y a toujours un biais vers /. Donc euh ouais c'est un bug qui devrait être corrigé donc / n'est pas tant favorisé, car idéalement les caractères de sel devraient être également pondérés.

26
thrig

mkpasswd(1) peut être un frontal pour crypt(3), mais ce n'est pas la même chose que d'exécuter chpasswd(1), qui fait partie du paquetage "shadow-utils" sur CentOS et "passwd" sur Debian. Au lieu de cela, vous devez comparer les pommes aux pommes. Considérez le script suivant:

#!/bin/bash

# This repeatedly changes a `saltuser' password
# and grabs the salt out of /etc/shadow.
# Requires root and the existence of `saltuser' user.

if [ $EUID -ne 0 ]; then
    echo "This script requires root access to read /etc/shadow."
    exit 1
fi

grep -q saltuser /etc/passwd

if [ $? -ne 0 ]; then
    echo "This script requires the 'saltuser' to be present."
    exit 2
fi

: > /tmp/salts.txt

for i in {1..1000}; do
    PW=$(tr -cd '[[:print:]]' < /dev/urandom | head -c 64)
    echo "saltuser:${PW}" | chpasswd -c SHA256 -s 0 2> /dev/urandom
    awk -F '$' '/^saltuser/ {print $3}' /etc/shadow >> /tmp/salts.txt
done

while read LINE; do
    # 6th character in the salt
    echo ${LINE:5:1}
done < /tmp/salts.txt | sort | uniq -c | sort -rn

Sortie de Debian Sid:

512 /
 14 T
 13 W
 13 v
 13 t
 12 x
 12 m
 12 d
 11 p
 11 L
 11 F
 11 4
 10 s
 10 l
 10 g
 10 f
 10 7
 10 6
  9 Z
  9 w
  9 N
  9 H
  9 G
  9 E
  9 A
  8 Y
  8 X
  8 r
  8 O
  8 j
  8 c
  8 B
  8 b
  8 9
  7 u
  7 R
  7 q
  7 P
  7 M
  7 k
  7 D
  6 z
  6 y
  6 U
  6 S
  6 K
  6 5
  5 V
  5 Q
  5 o
  5 J
  5 I
  5 i
  5 C
  5 a
  5 3
  4 n
  4 h
  4 e
  4 2
  4 0
  4 .
  3 8
  3 1

Sortie de CentOS 7:

504 /
 13 P
 13 B
 12 s
 12 Z
 11 e
 11 Y
 11 O
 11 L
 11 G
 10 w
 10 u
 10 q
 10 i
 10 h
 10 X
 10 I
 10 E
  9 x
  9 g
  9 f
  9 W
  9 F
  9 C
  9 9
  9 8
  8 v
  8 t
  8 c
  8 b
  8 S
  8 H
  8 D
  8 0
  7 z
  7 y
  7 o
  7 k
  7 U
  7 T
  7 R
  7 M
  7 A
  7 6
  7 4
  7 1
  6 p
  6 d
  6 a
  6 Q
  6 J
  6 5
  6 .
  5 r
  5 m
  5 j
  5 V
  5 3
  5 2
  4 n
  4 l
  4 N
  4 K
  3 7

Ainsi, le problème n'est pas propre à CentOS, mais vient probablement de l'amont d'où les deux projets tirent.

4
Aaron Toponce